package io.questdb.std;

import io.questdb.std.str.CharSink;
import io.questdb.std.str.Sinkable;
import io.questdb.std.str.StringSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

/**
 * Decimal256 - a mutable decimal number implementation. The value is a signed number with
 * two's complement representation.
 * <p>
 * This class represents decimal numbers with a fixed scale (number of decimal places)
 * using 256-bit integer arithmetic for precise calculations. All operations are
 * performed in-place to eliminate object allocation and improve performance.
 * </p>
 * <p>
 * This type tries to but doesn't follow IEEE 754; one of the main goals is using 256 bits to store
 * the sign and trailing significant field (T).
 * Using 1 bit for the sign, we have 255 bits for T, which gives us 77 digits of precision.
 * </p>
 */
public class Decimal256 implements Sinkable, Decimal {
    public static final int BYTES = 32;
    /**
     * Maximum allowed scale (number of decimal places)
     */
    public static final int MAX_SCALE = 76;
    public static final Decimal256 MAX_VALUE = new Decimal256(1593091911132452277L, 532749306367912313L, 8607968719199866879L, -1L, 0); // 10⁷⁶ - 1
    public static final Decimal256 MIN_VALUE = new Decimal256(-1593091911132452278L, -532749306367912314L, -8607968719199866880L, 1L, 0); // -10⁷⁶ + 1
    public static final Decimal256 NULL_VALUE = new Decimal256(Decimals.DECIMAL256_HH_NULL, Decimals.DECIMAL256_HL_NULL, Decimals.DECIMAL256_LH_NULL, Decimals.DECIMAL256_LL_NULL, 0);
    public static final Decimal256 ZERO = new Decimal256(0, 0, 0, 0, 0);
    // @formatter:off
    /**
     * Pre-computed powers of 10 table for decimal arithmetic.
     * Autogenerated. See `Decimal256Test.testPowersTenTable`.
     * 
     * <p>
     * This table stores the 256-bit representation of powers of 10 (10^0 to 10^76) 
     * multiplied by digits 1-9. Each row contains 9 complete 256-bit values, where
     * each 256-bit value is represented as 4 consecutive 64-bit longs:
     * - HH (bits 255-192, most significant 64 bits)
     * - HL (bits 191-128)
     * - LH (bits 127-64)  
     * - LL (bits 63-0, least significant 64 bits)
     * 
     * <p>
     * Structure: table[power][multiplier_offset + component]
     * - power: power of 10 (0 = 10^0, 1 = 10^1, ..., 76 = 10^76)
     * - multiplier_offset: (multiplier-1) * 4, where multiplier ∈ [1,9]
     * - component: 0=HH, 1=HL, 2=LH, 3=LL
     * 
     * <p>
     * Example: To get 7 × 10^25:
     * - Row: table[25]
     * - Offset: (7-1) * 4 = 24
     * - Access: HH=table[25][24], HL=table[25][25], LH=table[25][26], LL=table[25][27]
     * - Value: (HH << 192) | (HL << 128) | (LH << 64) | LL
     * 
     * <p>
     * This pre-computation enables fast multiplication by avoiding expensive
     * 256-bit arithmetic during decimal parsing and scaling operations.
     */
    private static final long[][] POWERS_TEN_TABLE = new long[][]{
            {0L, 0L, 0L, 1L, 0L, 0L, 0L, 2L, 0L, 0L, 0L, 3L, 0L, 0L, 0L, 4L, 0L, 0L, 0L, 5L, 0L, 0L, 0L, 6L, 0L, 0L, 0L, 7L, 0L, 0L, 0L, 8L, 0L, 0L, 0L, 9L},
            {0L, 0L, 0L, 10L, 0L, 0L, 0L, 20L, 0L, 0L, 0L, 30L, 0L, 0L, 0L, 40L, 0L, 0L, 0L, 50L, 0L, 0L, 0L, 60L, 0L, 0L, 0L, 70L, 0L, 0L, 0L, 80L, 0L, 0L, 0L, 90L},
            {0L, 0L, 0L, 100L, 0L, 0L, 0L, 200L, 0L, 0L, 0L, 300L, 0L, 0L, 0L, 400L, 0L, 0L, 0L, 500L, 0L, 0L, 0L, 600L, 0L, 0L, 0L, 700L, 0L, 0L, 0L, 800L, 0L, 0L, 0L, 900L},
            {0L, 0L, 0L, 1000L, 0L, 0L, 0L, 2000L, 0L, 0L, 0L, 3000L, 0L, 0L, 0L, 4000L, 0L, 0L, 0L, 5000L, 0L, 0L, 0L, 6000L, 0L, 0L, 0L, 7000L, 0L, 0L, 0L, 8000L, 0L, 0L, 0L, 9000L},
            {0L, 0L, 0L, 10000L, 0L, 0L, 0L, 20000L, 0L, 0L, 0L, 30000L, 0L, 0L, 0L, 40000L, 0L, 0L, 0L, 50000L, 0L, 0L, 0L, 60000L, 0L, 0L, 0L, 70000L, 0L, 0L, 0L, 80000L, 0L, 0L, 0L, 90000L},
            {0L, 0L, 0L, 100000L, 0L, 0L, 0L, 200000L, 0L, 0L, 0L, 300000L, 0L, 0L, 0L, 400000L, 0L, 0L, 0L, 500000L, 0L, 0L, 0L, 600000L, 0L, 0L, 0L, 700000L, 0L, 0L, 0L, 800000L, 0L, 0L, 0L, 900000L},
            {0L, 0L, 0L, 1000000L, 0L, 0L, 0L, 2000000L, 0L, 0L, 0L, 3000000L, 0L, 0L, 0L, 4000000L, 0L, 0L, 0L, 5000000L, 0L, 0L, 0L, 6000000L, 0L, 0L, 0L, 7000000L, 0L, 0L, 0L, 8000000L, 0L, 0L, 0L, 9000000L},
            {0L, 0L, 0L, 10000000L, 0L, 0L, 0L, 20000000L, 0L, 0L, 0L, 30000000L, 0L, 0L, 0L, 40000000L, 0L, 0L, 0L, 50000000L, 0L, 0L, 0L, 60000000L, 0L, 0L, 0L, 70000000L, 0L, 0L, 0L, 80000000L, 0L, 0L, 0L, 90000000L},
            {0L, 0L, 0L, 100000000L, 0L, 0L, 0L, 200000000L, 0L, 0L, 0L, 300000000L, 0L, 0L, 0L, 400000000L, 0L, 0L, 0L, 500000000L, 0L, 0L, 0L, 600000000L, 0L, 0L, 0L, 700000000L, 0L, 0L, 0L, 800000000L, 0L, 0L, 0L, 900000000L},
            {0L, 0L, 0L, 1000000000L, 0L, 0L, 0L, 2000000000L, 0L, 0L, 0L, 3000000000L, 0L, 0L, 0L, 4000000000L, 0L, 0L, 0L, 5000000000L, 0L, 0L, 0L, 6000000000L, 0L, 0L, 0L, 7000000000L, 0L, 0L, 0L, 8000000000L, 0L, 0L, 0L, 9000000000L},
            {0L, 0L, 0L, 10000000000L, 0L, 0L, 0L, 20000000000L, 0L, 0L, 0L, 30000000000L, 0L, 0L, 0L, 40000000000L, 0L, 0L, 0L, 50000000000L, 0L, 0L, 0L, 60000000000L, 0L, 0L, 0L, 70000000000L, 0L, 0L, 0L, 80000000000L, 0L, 0L, 0L, 90000000000L},
            {0L, 0L, 0L, 100000000000L, 0L, 0L, 0L, 200000000000L, 0L, 0L, 0L, 300000000000L, 0L, 0L, 0L, 400000000000L, 0L, 0L, 0L, 500000000000L, 0L, 0L, 0L, 600000000000L, 0L, 0L, 0L, 700000000000L, 0L, 0L, 0L, 800000000000L, 0L, 0L, 0L, 900000000000L},
            {0L, 0L, 0L, 1000000000000L, 0L, 0L, 0L, 2000000000000L, 0L, 0L, 0L, 3000000000000L, 0L, 0L, 0L, 4000000000000L, 0L, 0L, 0L, 5000000000000L, 0L, 0L, 0L, 6000000000000L, 0L, 0L, 0L, 7000000000000L, 0L, 0L, 0L, 8000000000000L, 0L, 0L, 0L, 9000000000000L},
            {0L, 0L, 0L, 10000000000000L, 0L, 0L, 0L, 20000000000000L, 0L, 0L, 0L, 30000000000000L, 0L, 0L, 0L, 40000000000000L, 0L, 0L, 0L, 50000000000000L, 0L, 0L, 0L, 60000000000000L, 0L, 0L, 0L, 70000000000000L, 0L, 0L, 0L, 80000000000000L, 0L, 0L, 0L, 90000000000000L},
            {0L, 0L, 0L, 100000000000000L, 0L, 0L, 0L, 200000000000000L, 0L, 0L, 0L, 300000000000000L, 0L, 0L, 0L, 400000000000000L, 0L, 0L, 0L, 500000000000000L, 0L, 0L, 0L, 600000000000000L, 0L, 0L, 0L, 700000000000000L, 0L, 0L, 0L, 800000000000000L, 0L, 0L, 0L, 900000000000000L},
            {0L, 0L, 0L, 1000000000000000L, 0L, 0L, 0L, 2000000000000000L, 0L, 0L, 0L, 3000000000000000L, 0L, 0L, 0L, 4000000000000000L, 0L, 0L, 0L, 5000000000000000L, 0L, 0L, 0L, 6000000000000000L, 0L, 0L, 0L, 7000000000000000L, 0L, 0L, 0L, 8000000000000000L, 0L, 0L, 0L, 9000000000000000L},
            {0L, 0L, 0L, 10000000000000000L, 0L, 0L, 0L, 20000000000000000L, 0L, 0L, 0L, 30000000000000000L, 0L, 0L, 0L, 40000000000000000L, 0L, 0L, 0L, 50000000000000000L, 0L, 0L, 0L, 60000000000000000L, 0L, 0L, 0L, 70000000000000000L, 0L, 0L, 0L, 80000000000000000L, 0L, 0L, 0L, 90000000000000000L},
            {0L, 0L, 0L, 100000000000000000L, 0L, 0L, 0L, 200000000000000000L, 0L, 0L, 0L, 300000000000000000L, 0L, 0L, 0L, 400000000000000000L, 0L, 0L, 0L, 500000000000000000L, 0L, 0L, 0L, 600000000000000000L, 0L, 0L, 0L, 700000000000000000L, 0L, 0L, 0L, 800000000000000000L, 0L, 0L, 0L, 900000000000000000L},
            {0L, 0L, 0L, 1000000000000000000L, 0L, 0L, 0L, 2000000000000000000L, 0L, 0L, 0L, 3000000000000000000L, 0L, 0L, 0L, 4000000000000000000L, 0L, 0L, 0L, 5000000000000000000L, 0L, 0L, 0L, 6000000000000000000L, 0L, 0L, 0L, 7000000000000000000L, 0L, 0L, 0L, 8000000000000000000L, 0L, 0L, 0L, 9000000000000000000L},
            {0L, 0L, 0L, -8446744073709551616L, 0L, 0L, 1L, 1553255926290448384L, 0L, 0L, 1L, -6893488147419103232L, 0L, 0L, 2L, 3106511852580896768L, 0L, 0L, 2L, -5340232221128654848L, 0L, 0L, 3L, 4659767778871345152L, 0L, 0L, 3L, -3786976294838206464L, 0L, 0L, 4L, 6213023705161793536L, 0L, 0L, 4L, -2233720368547758080L},
            {0L, 0L, 5L, 7766279631452241920L, 0L, 0L, 10L, -2914184810805067776L, 0L, 0L, 16L, 4852094820647174144L, 0L, 0L, 21L, -5828369621610135552L, 0L, 0L, 27L, 1937910009842106368L, 0L, 0L, 32L, -8742554432415203328L, 0L, 0L, 37L, -976274800962961408L, 0L, 0L, 43L, 6790004830489280512L, 0L, 0L, 48L, -3890459611768029184L},
            {0L, 0L, 54L, 3875820019684212736L, 0L, 0L, 108L, 7751640039368425472L, 0L, 0L, 162L, -6819284014656913408L, 0L, 0L, 216L, -2943463994972700672L, 0L, 0L, 271L, 932356024711512064L, 0L, 0L, 325L, 4808176044395724800L, 0L, 0L, 379L, 8683996064079937536L, 0L, 0L, 433L, -5886927989945401344L, 0L, 0L, 487L, -2011107970261188608L},
            {0L, 0L, 542L, 1864712049423024128L, 0L, 0L, 1084L, 3729424098846048256L, 0L, 0L, 1626L, 5594136148269072384L, 0L, 0L, 2168L, 7458848197692096512L, 0L, 0L, 2710L, -9123183826594430976L, 0L, 0L, 3252L, -7258471777171406848L, 0L, 0L, 3794L, -5393759727748382720L, 0L, 0L, 4336L, -3529047678325358592L, 0L, 0L, 4878L, -1664335628902334464L},
            {0L, 0L, 5421L, 200376420520689664L, 0L, 0L, 10842L, 400752841041379328L, 0L, 0L, 16263L, 601129261562068992L, 0L, 0L, 21684L, 801505682082758656L, 0L, 0L, 27105L, 1001882102603448320L, 0L, 0L, 32526L, 1202258523124137984L, 0L, 0L, 37947L, 1402634943644827648L, 0L, 0L, 43368L, 1603011364165517312L, 0L, 0L, 48789L, 1803387784686206976L},
            {0L, 0L, 54210L, 2003764205206896640L, 0L, 0L, 108420L, 4007528410413793280L, 0L, 0L, 162630L, 6011292615620689920L, 0L, 0L, 216840L, 8015056820827586560L, 0L, 0L, 271050L, -8427923047675068416L, 0L, 0L, 325260L, -6424158842468171776L, 0L, 0L, 379470L, -4420394637261275136L, 0L, 0L, 433680L, -2416630432054378496L, 0L, 0L, 487890L, -412866226847481856L},
            {0L, 0L, 542101L, 1590897978359414784L, 0L, 0L, 1084202L, 3181795956718829568L, 0L, 0L, 1626303L, 4772693935078244352L, 0L, 0L, 2168404L, 6363591913437659136L, 0L, 0L, 2710505L, 7954489891797073920L, 0L, 0L, 3252606L, -8901356203553062912L, 0L, 0L, 3794707L, -7310458225193648128L, 0L, 0L, 4336808L, -5719560246834233344L, 0L, 0L, 4878909L, -4128662268474818560L},
            {0L, 0L, 5421010L, -2537764290115403776L, 0L, 0L, 10842021L, -5075528580230807552L, 0L, 0L, 16263032L, -7613292870346211328L, 0L, 0L, 21684043L, 8295686913247936512L, 0L, 0L, 27105054L, 5757922623132532736L, 0L, 0L, 32526065L, 3220158333017128960L, 0L, 0L, 37947076L, 682394042901725184L, 0L, 0L, 43368086L, -1855370247213678592L, 0L, 0L, 48789097L, -4393134537329082368L},
            {0L, 0L, 54210108L, -6930898827444486144L, 0L, 0L, 108420217L, 4584946418820579328L, 0L, 0L, 162630325L, -2345952408623906816L, 0L, 0L, 216840434L, 9169892837641158656L, 0L, 0L, 271050543L, 2238994010196672512L, 0L, 0L, 325260651L, -4691904817247813632L, 0L, 0L, 379470760L, 6823940429017251840L, 0L, 0L, 433680868L, -106958398427234304L, 0L, 0L, 487890977L, -7037857225871720448L},
            {0L, 0L, 542101086L, 4477988020393345024L, 0L, 0L, 1084202172L, 8955976040786690048L, 0L, 0L, 1626303258L, -5012780012529516544L, 0L, 0L, 2168404344L, -534791992136171520L, 0L, 0L, 2710505431L, 3943196028257173504L, 0L, 0L, 3252606517L, 8421184048650518528L, 0L, 0L, 3794707603L, -5547572004665688064L, 0L, 0L, 4336808689L, -1069583984272343040L, 0L, 0L, 4878909776L, 3408404036121001984L},
            {0L, 0L, 5421010862L, 7886392056514347008L, 0L, 0L, 10842021724L, -2673959960680857600L, 0L, 0L, 16263032587L, 5212432095833489408L, 0L, 0L, 21684043449L, -5347919921361715200L, 0L, 0L, 27105054312L, 2538472135152631808L, 0L, 0L, 32526065174L, -8021879882042572800L, 0L, 0L, 37947076036L, -135487825528225792L, 0L, 0L, 43368086899L, 7750904230986121216L, 0L, 0L, 48789097761L, -2809447786209083392L},
            {0L, 0L, 54210108624L, 5076944270305263616L, 0L, 0L, 108420217248L, -8292855533099024384L, 0L, 0L, 162630325872L, -3215911262793760768L, 0L, 0L, 216840434497L, 1861033007511502848L, 0L, 0L, 271050543121L, 6937977277816766464L, 0L, 0L, 325260651745L, -6431822525587521536L, 0L, 0L, 379470760369L, -1354878255282257920L, 0L, 0L, 433680868994L, 3722066015023005696L, 0L, 0L, 487890977618L, 8799010285328269312L},
            {0L, 0L, 542101086242L, -4570789518076018688L, 0L, 0L, 1084202172485L, -9141579036152037376L, 0L, 0L, 1626303258728L, 4734375519481495552L, 0L, 0L, 2168404344971L, 163586001405476864L, 0L, 0L, 2710505431213L, -4407203516670541824L, 0L, 0L, 3252606517456L, -8977993034746560512L, 0L, 0L, 3794707603699L, 4897961520886972416L, 0L, 0L, 4336808689942L, 327172002810953728L, 0L, 0L, 4878909776184L, -4243617515265064960L},
            {0L, 0L, 5421010862427L, -8814407033341083648L, 0L, 0L, 10842021724855L, 817930007027384320L, 0L, 0L, 16263032587282L, -7996477026313699328L, 0L, 0L, 21684043449710L, 1635860014054768640L, 0L, 0L, 27105054312137L, -7178547019286315008L, 0L, 0L, 32526065174565L, 2453790021082152960L, 0L, 0L, 37947076036992L, -6360617012258930688L, 0L, 0L, 43368086899420L, 3271720028109537280L, 0L, 0L, 48789097761847L, -5542687005231546368L},
            {0L, 0L, 54210108624275L, 4089650035136921600L, 0L, 0L, 108420217248550L, 8179300070273843200L, 0L, 0L, 162630325872825L, -6177793968298786816L, 0L, 0L, 216840434497100L, -2088143933161865216L, 0L, 0L, 271050543121376L, 2001506101975056384L, 0L, 0L, 325260651745651L, 6091156137111977984L, 0L, 0L, 379470760369926L, -8265937901460652032L, 0L, 0L, 433680868994201L, -4176287866323730432L, 0L, 0L, 487890977618476L, -86637831186808832L},
            {0L, 0L, 542101086242752L, 4003012203950112768L, 0L, 0L, 1084202172485504L, 8006024407900225536L, 0L, 0L, 1626303258728256L, -6437707461859213312L, 0L, 0L, 2168404344971008L, -2434695257909100544L, 0L, 0L, 2710505431213761L, 1568316946041012224L, 0L, 0L, 3252606517456513L, 5571329149991124992L, 0L, 0L, 3794707603699265L, -8872402719768313856L, 0L, 0L, 4336808689942017L, -4869390515818201088L, 0L, 0L, 4878909776184769L, -866378311868088320L},
            {0L, 0L, 5421010862427522L, 3136633892082024448L, 0L, 0L, 10842021724855044L, 6273267784164048896L, 0L, 0L, 16263032587282566L, -9036842397463478272L, 0L, 0L, 21684043449710088L, -5900208505381453824L, 0L, 0L, 27105054312137610L, -2763574613299429376L, 0L, 0L, 32526065174565133L, 373059278782595072L, 0L, 0L, 37947076036992655L, 3509693170864619520L, 0L, 0L, 43368086899420177L, 6646327062946643968L, 0L, 0L, 48789097761847699L, -8663783118680883200L},
            {0L, 0L, 54210108624275221L, -5527149226598858752L, 0L, 0L, 108420217248550443L, 7392445620511834112L, 0L, 0L, 162630325872825665L, 1865296393912975360L, 0L, 0L, 216840434497100886L, -3661852832685883392L, 0L, 0L, 271050543121376108L, -9189002059284742144L, 0L, 0L, 325260651745651330L, 3730592787825950720L, 0L, 0L, 379470760369926551L, -1796556438772908032L, 0L, 0L, 433680868994201773L, -7323705665371766784L, 0L, 0L, 487890977618476995L, 5595889181738926080L},
            {0L, 0L, 542101086242752217L, 68739955140067328L, 0L, 0L, 1084202172485504434L, 137479910280134656L, 0L, 0L, 1626303258728256651L, 206219865420201984L, 0L, 0L, 2168404344971008868L, 274959820560269312L, 0L, 0L, 2710505431213761085L, 343699775700336640L, 0L, 0L, 3252606517456513302L, 412439730840403968L, 0L, 0L, 3794707603699265519L, 481179685980471296L, 0L, 0L, 4336808689942017736L, 549919641120538624L, 0L, 0L, 4878909776184769953L, 618659596260605952L},
            {0L, 0L, 5421010862427522170L, 687399551400673280L, 0L, 0L, -7604722348854507276L, 1374799102801346560L, 0L, 0L, -2183711486426985106L, 2062198654202019840L, 0L, 1L, 3237299376000537064L, 2749598205602693120L, 0L, 1L, 8658310238428059234L, 3436997757003366400L, 0L, 1L, -4367422972853970212L, 4124397308404039680L, 0L, 2L, 1053587889573551958L, 4811796859804712960L, 0L, 2L, 6474598752001074128L, 5499196411205386240L, 0L, 2L, -6551134459280955318L, 6186595962606059520L},
            {0L, 2L, -1130123596853433148L, 6873995514006732800L, 0L, 5L, -2260247193706866296L, -4698753045696086016L, 0L, 8L, -3390370790560299443L, 2175242468310646784L, 0L, 11L, -4520494387413732591L, 9049237982317379584L, 0L, 14L, -5650617984267165739L, -2523510577385439232L, 0L, 17L, -6780741581120598886L, 4350484936621293568L, 0L, 20L, -7910865177974032034L, -7222263623081525248L, 0L, 23L, -9040988774827465182L, -348268109074792448L, 0L, 26L, 8275631702028653287L, 6525727404931940352L},
            {0L, 29L, 7145508105175220139L, -5047021154770878464L, 0L, 58L, -4155727863359111337L, 8352701764167794688L, 0L, 88L, 2989780241816108803L, 3305680609396916224L, 0L, 117L, -8311455726718222674L, -1741340545373962240L, 0L, 146L, -1165947621543002534L, -6788361700144840704L, 0L, 176L, 5979560483632217606L, 6611361218793832448L, 0L, 205L, -5321675484902113870L, 1564340064022953984L, 0L, 235L, 1823832620273106269L, -3482681090747924480L, 0L, 264L, 8969340725448326409L, -8529702245518802944L},
            {0L, 293L, -2331895243086005067L, 4870020673419870208L, 0L, 587L, -4663790486172010134L, -8706702726869811200L, 0L, 881L, -6995685729258015201L, -3836682053449940992L, 0L, 1175L, 9119163101365531349L, 1033338619969929216L, 0L, 1469L, 6787267858279526282L, 5903359293389799424L, 0L, 1763L, 4455372615193521215L, -7673364106899881984L, 0L, 2057L, 2123477372107516148L, -2803343433480011776L, 0L, 2350L, -208417870978488918L, 2066677239939858432L, 0L, 2644L, -2540313114064493985L, 6936697913359728640L},
            {0L, 2938L, -4872208357150499052L, -6640025486929952768L, 0L, 5877L, 8702327359408553513L, 5166693099849646080L, 0L, 8816L, 3830119002258054461L, -1473332387080306688L, 0L, 11754L, -1042089354892444590L, -8113357874010259456L, 0L, 14693L, -5914297712042943641L, 3693360712769339392L, 0L, 17632L, 7660238004516108923L, -2946664774160613376L, 0L, 20571L, 2788029647365609872L, 8860053812618985472L, 0L, 23509L, -2084178709784889179L, 2220028325689032704L, 0L, 26448L, -6956387066935388231L, -4419997161240920064L},
            {0L, 29387L, 6618148649623664334L, 7386721425538678784L, 0L, 58774L, -5210446774462222948L, -3673301222632194048L, 0L, 88162L, 1407701875161441387L, 3713420202906484736L, 0L, 117549L, 8025850524785105721L, -7346602445264388096L, 0L, 146936L, -3802744899300781560L, 40118980274290688L, 0L, 176324L, 2815403750322882774L, 7426840405812969472L, 0L, 205711L, -9013191673763004508L, -3633182242357903360L, 0L, 235098L, -2395043024139340173L, 3753539183180775424L, 0L, 264486L, 4223105625484324161L, -7306483464990097408L},
            {0L, 293873L, -7605489798601563120L, 80237960548581376L, 0L, 587747L, 3235764476506425376L, 160475921097162752L, 0L, 881620L, -4369725322095137744L, 240713881645744128L, 0L, 1175494L, 6471528953012850752L, 320951842194325504L, 0L, 1469367L, -1133960845588712368L, 401189802742906880L, 0L, 1763241L, -8739450644190275488L, 481427763291488256L, 0L, 2057115L, 2101803630917713008L, 561665723840069632L, 0L, 2350988L, -5503686167683850112L, 641903684388651008L, 0L, 2644862L, 5337568107424138384L, 722141644937232384L},
            {0L, 2938735L, -2267921691177424736L, 802379605485813760L, 0L, 5877471L, -4535843382354849472L, 1604759210971627520L, 0L, 8816207L, -6803765073532274208L, 2407138816457441280L, 0L, 11754943L, -9071686764709698944L, 3209518421943255040L, 0L, 14693679L, 7107135617822427936L, 4011898027429068800L, 0L, 17632415L, 4839213926645003200L, 4814277632914882560L, 0L, 20571151L, 2571292235467578464L, 5616657238400696320L, 0L, 23509887L, 303370544290153728L, 6419036843886510080L, 0L, 26448622L, -1964551146887271008L, 7221416449372323840L},
            {0L, 29387358L, -4232472838064695744L, 8023796054858137600L, 0L, 58774717L, -8464945676129391488L, -2399151963993276416L, 0L, 88162076L, 5749325559515464385L, 5624644090864861184L, 0L, 117549435L, 1516852721450768641L, -4798303927986552832L, 0L, 146936793L, -2715620116613927102L, 3225492126871584768L, 0L, 176324152L, -6948092954678622846L, -7197455891979829248L, 0L, 205711511L, 7266178280966233027L, 826340162878308352L, 0L, 235098870L, 3033705442901537283L, 8850136217736445952L, 0L, 264486228L, -1198767395163158461L, -1572811801114968064L},
            {0L, 293873587L, -5431240233227854204L, 6450984253743169536L, 0L, 587747175L, 7584263607253843208L, -5544775566223212544L, 0L, 881620763L, 2153023374025989005L, 906208687519956992L, 0L, 1175494350L, -3278216859201865199L, 7357192941263126528L, 0L, 1469367938L, -8709457092429719403L, -4638566878703255552L, 0L, 1763241526L, 4306046748051978010L, 1812417375039913984L, 0L, 2057115113L, -1125193485175876194L, 8263401628783083520L, 0L, 2350988701L, -6556433718403730398L, -3732358191183298560L, 0L, 2644862289L, 6459070122077967015L, 2718626062559870976L},
            {0L, 2938735877L, 1027829888850112811L, 9169610316303040512L, 0L, 5877471754L, 2055659777700225622L, -107523441103470592L, 0L, 8816207631L, 3083489666550338434L, 9062086875199569920L, 0L, 11754943508L, 4111319555400451245L, -215046882206941184L, 0L, 14693679385L, 5139149444250564057L, 8954563434096099328L, 0L, 17632415262L, 6166979333100676868L, -322570323310411776L, 0L, 20571151139L, 7194809221950789680L, 8847039992992628736L, 0L, 23509887016L, 8222639110800902491L, -430093764413882368L, 0L, 26448622893L, -9196275074058536313L, 8739516551889158144L},
            {0L, 29387358770L, -8168445185208423502L, -537617205517352960L, 0L, 58774717541L, 2109853703292704613L, -1075234411034705920L, 0L, 88162076311L, -6058591481915718888L, -1612851616552058880L, 0L, 117549435082L, 4219707406585409227L, -2150468822069411840L, 0L, 146936793852L, -3948737778623014274L, -2688086027586764800L, 0L, 176324152623L, 6329561109878113841L, -3225703233104117760L, 0L, 205711511393L, -1838884075330309660L, -3763320438621470720L, 0L, 235098870164L, 8439414813170818455L, -4300937644138823680L, 0L, 264486228935L, 270969627962394954L, -4838554849656176640L},
            {0L, 293873587705L, -7897475557246028547L, -5376172055173529600L, 0L, 587747175411L, 2651792959217494523L, 7694399963362492416L, 0L, 881620763116L, -5245682598028534023L, 2318227908188962816L, 0L, 1175494350822L, 5303585918434989046L, -3057944146984566784L, 0L, 1469367938527L, -2593889638811039500L, -8434116202158096384L, 0L, 1763241526233L, 7955378877652483570L, 4636455816377925632L, 0L, 2057115113939L, 57903320406455023L, -739716238795603968L, 0L, 2350988701644L, -7839572236839573523L, -6115888293969133568L, 0L, 2644862289350L, 2709696279623949547L, 6954683724566888448L},
            {0L, 2938735877055L, -5187779277622078999L, 1578511669393358848L, 0L, 5877471754111L, 8071185518465393618L, 3157023338786717696L, 0L, 8816207631167L, 2883406240843314619L, 4735535008180076544L, 0L, 11754943508222L, -2304373036778764380L, 6314046677573435392L, 0L, 14693679385278L, -7492152314400843379L, 7892558346966794240L, 0L, 17632415262334L, 5766812481686629238L, -8975674057349398528L, 0L, 20571151139390L, 579033204064550239L, -7397162387956039680L, 0L, 23509887016445L, -4608746073557528760L, -5818650718562680832L, 0L, 26448622893501L, 8650218722529943857L, -4240139049169321984L},
            {0L, 29387358770557L, 3462439444907864858L, -2661627379775963136L, 0L, 58774717541114L, 6924878889815729717L, -5323254759551926272L, 0L, 88162076311671L, -8059425738985957040L, -7984882139327889408L, 0L, 117549435082228L, -4596986294078092181L, 7800234554605699072L, 0L, 146936793852785L, -1134546849170227322L, 5138607174829735936L, 0L, 176324152623343L, 2327892595737637537L, 2476979795053772800L, 0L, 205711511393900L, 5790332040645502395L, -184647584722190336L, 0L, 235098870164457L, -9193972588156184362L, -2846274964498153472L, 0L, 264486228935014L, -5731533143248319503L, -5507902344274116608L},
            {0L, 293873587705571L, -2269093698340454644L, -8169529724050079744L, 0L, 587747175411143L, -4538187396680909287L, 2107684625609392128L, 0L, 881620763116715L, -6807281095021363931L, -6061845098440687616L, 0L, 1175494350822287L, -9076374793361818574L, 4215369251218784256L, 0L, 1469367938527859L, 7101275582007278398L, -3954160472831295488L, 0L, 1763241526233431L, 4832181883666823755L, 6323053876828176384L, 0L, 2057115113939003L, 2563088185326369111L, -1846475847221903360L, 0L, 2350988701644575L, 293994486985914468L, 8430738502437568512L, 0L, 2644862289350146L, -1975099211354540175L, 261208778387488768L},
            {0L, 2938735877055718L, -4244192909694994819L, -7908320945662590976L, 0L, 5877471754111437L, -8488385819389989637L, 2630102182384369664L, 0L, 8816207631167156L, 5714165344624567160L, -5278218763278221312L, 0L, 11754943508222875L, 1469972434929572342L, 5260204364768739328L, 0L, 14693679385278593L, -2774220474765422477L, -2648116580893851648L, 0L, 17632415262334312L, -7018413384460417295L, 7890306547153108992L, 0L, 20571151139390031L, 7184137779554139502L, -18014398509481984L, 0L, 23509887016445750L, 2939944869859144684L, -7926335344172072960L, 0L, 26448622893501468L, -1304248039835850134L, 2612087783874887680L},
            {0L, 29387358770557187L, -5548440949530844953L, -5296233161787703296L, 0L, 58774717541114375L, 7349862174647861711L, 7854277750134145024L, 0L, 88162076311671563L, 1801421225117016759L, 2558044588346441728L, 0L, 117549435082228750L, -3747019724413828194L, -2738188573441261568L, 0L, 146936793852785938L, 9151283399764878470L, -8034421735228964864L, 0L, 176324152623343126L, 3602842450234033518L, 5116089176692883456L, 0L, 205711511393900313L, -1945598499296811435L, -180143985094819840L, 0L, 235098870164457501L, -7494039448827656387L, -5476377146882523136L, 0L, 264486228935014689L, 5404263675351050277L, 7674133765039325184L},
            {0L, 293873587705571876L, -144177274179794675L, 2377900603251621888L, 0L, 587747175411143753L, -288354548359589350L, 4755801206503243776L, 0L, 881620763116715630L, -432531822539384025L, 7133701809754865664L, 0L, 1175494350822287507L, -576709096719178700L, -8935141660703064064L, 0L, 1469367938527859384L, -720886370898973375L, -6557241057451442176L, 0L, 1763241526233431261L, -865063645078768050L, -4179340454199820288L, 0L, 2057115113939003138L, -1009240919258562725L, -1801439850948198400L, 0L, 2350988701644575015L, -1153418193438357399L, 576460752303423488L, 0L, 2644862289350146892L, -1297595467618152074L, 2954361355555045376L},
            {0L, 2938735877055718769L, -1441772741797946749L, 5332261958806667264L, 0L, 5877471754111437539L, -2883545483595893498L, -7782220156096217088L, 0L, 8816207631167156309L, -4325318225393840247L, -2449958197289549824L, 0L, -6691800565486676537L, -5767090967191786995L, 2882303761517117440L, 0L, -3753064688430957767L, -7208863708989733744L, 8214565720323784704L, 0L, -814328811375238997L, -8650636450787680493L, -4899916394579099648L, 1L, 2124407065680479773L, 8354334881123924375L, 432345564227567616L, 1L, 5063142942736198543L, 6912562139325977626L, 5764607523034234880L, 1L, 8001878819791917313L, 5470789397528030877L, -7349874591868649472L},
            {1L, -7506129376861915533L, 4029016655730084128L, -2017612633061982208L, 3L, 3434485319985720550L, 8058033311460168257L, -4035225266123964416L, 4L, -4071644056876194983L, -6359694106519299230L, -6052837899185946624L, 6L, 6868970639971441100L, -2330677450789215101L, -8070450532247928832L, 7L, -637158736890474432L, 1698339204940869028L, 8358680908399640576L, 9L, -8143288113752389965L, 5727355860670953157L, 6341068275337658368L, 11L, 2797326583095246118L, -8690371557308514330L, 4323455642275676160L, 12L, -4708802793766669415L, -4661354901578430201L, 2305843009213693952L, 14L, 6231811903080966668L, -632338245848346072L, 288230376151711744L},
            {15L, -1274317473780948864L, 3396678409881738056L, -1729382256910270464L, 31L, -2548634947561897728L, 6793356819763476113L, -3458764513820540928L, 47L, -3822952421342846592L, -8256708844064337446L, -5188146770730811392L, 63L, -5097269895123795456L, -4860030434182599389L, -6917529027641081856L, 79L, -6371587368904744320L, -1463352024300861332L, -8646911284551352320L, 95L, -7645904842685693183L, 1933326385580876725L, 8070450532247928832L, 111L, -8920222316466642047L, 5330004795462614782L, 6341068275337658368L, 127L, 8252204283461960705L, 8726683205344352839L, 4611686018427387904L, 143L, 6977886809681011841L, -6323382458483460720L, 2882303761517117440L},
            {159L, 5703569335900062977L, -2926704048601722663L, 1152921504606846976L, 318L, -7039605401909425661L, -5853408097203445326L, 2305843009213693952L, 477L, -1336036066009362683L, -8780112145805167989L, 3458764513820540928L, 637L, 4367533269890700295L, 6739927879302660964L, 4611686018427387904L, 796L, -8375641467918788343L, 3813223830700938301L, 5764607523034234880L, 955L, -2672072132018725365L, 886519782099215638L, 6917529027641081856L, 1115L, 3031497203881337612L, -2040184266502507025L, 8070450532247928832L, 1274L, 8735066539781400590L, -4966888315104229688L, -9223372036854775808L, 1433L, -4008108198028088048L, -7893592363705952351L, -8070450532247928832L},
            {1593L, 1695461137871974930L, 7626447661401876602L, -6917529027641081856L, 3186L, 3390922275743949860L, -3193848750905798411L, 4611686018427387904L, 4779L, 5086383413615924791L, 4432598910496078191L, -2305843009213693952L, 6372L, 6781844551487899721L, -6387697501811596822L, -9223372036854775808L, 7965L, 8477305689359874652L, 1238750159590279781L, 2305843009213693952L, 9558L, -8273977246477702034L, 8865197820992156383L, -4611686018427387904L, 11151L, -6578516108605727104L, -1955098591315518630L, 6917529027641081856L, 12744L, -4883054970733752173L, 5671349070086357973L, 0L, 14337L, -3187593832861777243L, -5148947342221317041L, -6917529027641081856L},
            {15930L, -1492132694989802312L, 2477500319180559562L, 4611686018427387904L, 31861L, -2984265389979604624L, 4955000638361119124L, -9223372036854775808L, 47792L, -4476398084969406936L, 7432500957541678686L, -4611686018427387904L, 63723L, -5968530779959209248L, -8536742796987313367L, 0L, 79654L, -7460663474949011560L, -6059242477806753805L, 4611686018427387904L, 95585L, -8952796169938813872L, -3581742158626194243L, -9223372036854775808L, 111516L, 8001815208780935432L, -1104241839445634681L, -4611686018427387904L, 127447L, 6509682513791133121L, 1373258479734924882L, 0L, 143378L, 5017549818801330809L, 3850758798915484444L, 4611686018427387904L},
            {159309L, 3525417123811528497L, 6328259118096044006L, -9223372036854775808L, 318618L, 7050834247623056994L, -5790225837517463603L, 0L, 477927L, -7870492702274966124L, 538033280578580403L, -9223372036854775808L, 637236L, -4345075578463437627L, 6866292398674624410L, 0L, 796545L, -819658454651909130L, -5252192556938883200L, -9223372036854775808L, 955855L, 2705758669159619368L, 1076066561157160807L, 0L, 1115164L, 6231175792971147865L, 7404325679253204813L, -9223372036854775808L, 1274473L, -8690151156926875254L, -4714159276360302796L, 0L, 1433782L, -5164734033115346756L, 1614099841735741210L, -9223372036854775808L},
            {1593091L, -1639316909303818259L, 7942358959831785217L, 0L, 3186183L, -3278633818607636518L, -2562026154045981182L, 0L, 4779275L, -4917950727911454776L, 5380332805785804035L, 0L, 6372367L, -6557267637215273035L, -5124052308091962364L, 0L, 7965459L, -8196584546519091293L, 2818306651739822853L, 0L, 9558551L, 8610842617886642064L, -7686078462137943546L, 0L, 11151643L, 6971525708582823806L, 256280497693841671L, 0L, 12744735L, 5332208799279005547L, 8198639457525626888L, 0L, 14337827L, 3692891889975187288L, -2305745656352139511L, 0L},
            {15930919L, 2053574980671369030L, 5636613303479645706L, 0L, 31861838L, 4107149961342738060L, -7173517466750260204L, 0L, 47792757L, 6160724942014107090L, -1536904163270614498L, 0L, 63723676L, 8214299922685476121L, 4099709140209031208L, 0L, 79654595L, -8178869170352706465L, -8710421630020874702L, 0L, 95585514L, -6125294189681337435L, -3073808326541228996L, 0L, 111516433L, -4071719209009968404L, 2562804976938416710L, 0L, 127447352L, -2018144228338599374L, 8199418280418062416L, 0L, 143378272L, 35430752332769656L, -4610712489811843494L, 0L},
            {159309191L, 2089005733004138687L, 1025900813667802212L, 0L, 318618382L, 4178011466008277374L, 2051801627335604424L, 0L, 477927573L, 6267017199012416061L, 3077702441003406636L, 0L, 637236764L, 8356022932016554748L, 4103603254671208848L, 0L, 796545955L, -8001715408688858181L, 5129504068339011060L, 0L, 955855146L, -5912709675684719494L, 6155404882006813272L, 0L, 1115164337L, -3823703942680580807L, 7181305695674615484L, 0L, 1274473528L, -1734698209676442120L, 8207206509342417696L, 0L, 1433782720L, 354307523327696567L, -9213636750699331708L, 0L},
            {1593091911L, 2443313256331835254L, -8187735937031529496L, 0L, 3186183822L, 4886626512663670509L, 2071272199646492624L, 0L, 4779275733L, 7329939768995505763L, -6116463737385036872L, 0L, 6372367644L, -8673491048382210598L, 4142544399292985248L, 0L, 7965459555L, -6230177792050375344L, -4045191537738544248L, 0L, 9558551466L, -3786864535718540089L, 6213816598939477872L, 0L, 11151643377L, -1343551279386704835L, -1973919338092051624L, 0L, 12744735289L, 1099761976945130420L, 8285088798585970496L, 0L, 14337827200L, 3543075233276965675L, 97352861554441000L, 0L},
            {15930919111L, 5986388489608800929L, -8090383075477088496L, 0L, 31861838222L, -6473967094491949757L, 2265977922755374624L, 0L, 47792757333L, -487578604883148828L, -5824405152721713872L, 0L, 63723676445L, 5498809884725652102L, 4531955845510749248L, 0L, 79654595556L, -6961545699375098585L, -3558427229966339248L, 0L, 95585514667L, -975157209766297655L, 6797933768266123872L, 0L, 111516433779L, 5011231279842503274L, -1292449307210964624L, 0L, 127447352890L, -7449124304258247412L, 9063911691021498496L, 0L, 143378272001L, -1462735814649446482L, 973528615544410000L, 0L},
            {159309191113L, 4523652674959354447L, -7116854459932678496L, 0L, 318618382226L, 9047305349918708895L, 4213035153844194624L, 0L, 477927573339L, -4875786048831488274L, -2903819306088483872L, 0L, 637236764452L, -352133373872133826L, 8426070307688389248L, 0L, 796545955566L, 4171519301087220622L, 1309215847755710752L, 0L, 955855146679L, 8695171976046575069L, -5807638612176967744L, 0L, 1115164337792L, -5227919422703622099L, 5522251001599905376L, 0L, 1274473528905L, -704266747744267652L, -1594603458332773120L, 0L, 1433782720019L, 3819385927215086796L, -8711457918265451616L, 0L},
            {1593091911132L, 8343038602174441244L, 2618431695511421504L, 0L, 3186183822264L, -1760666869360669128L, 5236863391022843008L, 0L, 4779275733397L, 6582371732813772116L, 7855295086534264512L, 0L, 6372367644529L, -3521333738721338256L, -7973017291663865600L, 0L, 7965459555662L, 4821704863453102988L, -5354585596152444096L, 0L, 9558551466794L, -5282000608082007384L, -2736153900641022592L, 0L, 11151643377927L, 3061037994092433860L, -117722205129601088L, 0L, 12744735289059L, -7042667477442676511L, 2500709490381820416L, 0L, 14337827200192L, 1300371124731764733L, 5119141185893241920L, 0L},
            {15930919111324L, -8803334346803345639L, 7737572881404663424L, 0L, 31861838222649L, 840075380102860338L, -2971598310900224768L, 0L, 47792757333973L, -7963258966700485300L, 4765974570504438656L, 0L, 63723676445298L, 1680150760205720677L, -5943196621800449536L, 0L, 79654595556622L, -7123183586597624961L, 1794376259604213888L, 0L, 95585514667947L, 2520226140308581016L, -8914794932700674304L, 0L, 111516433779271L, -6283108206494764623L, -1177222051296010880L, 0L, 127447352890596L, 3360301520411441355L, 6560350830108652544L, 0L, 143378272001920L, -5443032826391904284L, -4148820362196235648L, 0L},
            {159309191113245L, 4200376900514301694L, 3588752519208427776L, 0L, 318618382226490L, 8400753801028603388L, 7177505038416855552L, 0L, 477927573339735L, -5845613372166646534L, -7680486516084268288L, 0L, 637236764452980L, -1645236471652344840L, -4091733996875840512L, 0L, 796545955566226L, 2555140428861956854L, -502981477667412736L, 0L, 955855146679471L, 6755517329376258549L, 3085771041541015040L, 0L, 1115164337792716L, -7490849843818991373L, 6674523560749442816L, 0L, 1274473528905961L, -3290472943304689679L, -8183467993751681024L, 0L, 1433782720019207L, 909903957209612015L, -4594715474543253248L, 0L},
            {1593091911132452L, 5110280857723913709L, -1005962955334825472L, 0L, 3186183822264904L, -8226182358261724197L, -2011925910669650944L, 0L, 4779275733397356L, -3115901500537810487L, -3017888866004476416L, 0L, 6372367644529809L, 1994379357186103223L, -4023851821339301888L, 0L, 7965459555662261L, 7104660214910016933L, -5029814776674127360L, 0L, 9558551466794713L, -6231803001075620973L, -6035777732008952832L, 0L, 11151643377927165L, -1121522143351707263L, -7041740687343778304L, 0L, 12744735289059618L, 3988758714372206447L, -8047703642678603776L, 0L, 14337827200192070L, 9099039572096120157L, -9053666598013429248L, 0L},
            {15930919111324522L, -4237423643889517749L, 8387114520361296896L, 0L, 31861838222649045L, -8474847287779035498L, -1672515032986957824L, 0L, 47792757333973568L, 5734473142040998370L, 6714599487374339072L, 0L, 63723676445298091L, 1497049498151480621L, -3345030065973915648L, 0L, 79654595556622613L, -2740374145738037127L, 5042084454387381248L, 0L, 95585514667947136L, -6977797789627554876L, -5017545098960873472L, 0L, 111516433779271659L, 7231522640192478992L, 3369569421400423424L, 0L, 127447352890596182L, 2994098996302961243L, -6690060131947831296L, 0L, 143378272001920704L, -1243324647586556505L, 1697054388413465600L, 0L},
            {159309191113245227L, -5480748291476074254L, -8362575164934789120L, 0L, 318618382226490455L, 7485247490757403109L, 1721593743839973376L, 0L, 477927573339735683L, 2004499199281328855L, -6640981421094815744L, 0L, 637236764452980910L, -3476249092194745398L, 3443187487679946752L, 0L, 796545955566226138L, -8956997383670819652L, -4919387677254842368L, 0L, 955855146679471366L, 4008998398562657711L, 5164781231519920128L, 0L, 1115164337792716593L, -1471749892913416543L, -3197793933414868992L, 0L, 1274473528905961821L, -6952498184389490796L, 6886374975359893504L, 0L, 1433782720019207049L, 6013497597843986566L, -1476200189574895616L, 0L}
    };
    // @formatter:on
    private static final long[][] POWERS_TEN_TABLE_THRESHOLDS = new long[][]{
            {159309191113245227L, -5480748291476074254L, -8362575164934789121L, -1L},
            {15930919111324522L, -4237423643889517749L, 8387114520361296895L, -1L},
            {1593091911132452L, 5110280857723913709L, -1005962955334825473L, -1L},
            {159309191113245L, 4200376900514301694L, 3588752519208427775L, -1L},
            {15930919111324L, -8803334346803345639L, 7737572881404663423L, -1L},
            {1593091911132L, 8343038602174441244L, 2618431695511421503L, -1L},
            {159309191113L, 4523652674959354447L, -7116854459932678497L, -1L},
            {15930919111L, 5986388489608800929L, -8090383075477088497L, -1L},
            {1593091911L, 2443313256331835254L, -8187735937031529497L, -1L},
            {159309191L, 2089005733004138687L, 1025900813667802211L, -1L},
            {15930919L, 2053574980671369030L, 5636613303479645705L, -1L},
            {1593091L, -1639316909303818259L, 7942358959831785216L, -1L},
            {159309L, 3525417123811528497L, 6328259118096044006L, 9223372036854775807L},
            {15930L, -1492132694989802312L, 2477500319180559562L, 4611686018427387903L},
            {1593L, 1695461137871974930L, 7626447661401876602L, -6917529027641081857L},
            {159L, 5703569335900062977L, -2926704048601722663L, 1152921504606846975L},
            {15L, -1274317473780948864L, 3396678409881738056L, -1729382256910270465L},
            {1L, -7506129376861915533L, 4029016655730084128L, -2017612633061982209L},
            {0L, 2938735877055718769L, -1441772741797946749L, 5332261958806667263L},
            {0L, 293873587705571876L, -144177274179794675L, 2377900603251621887L},
            {0L, 29387358770557187L, -5548440949530844953L, -5296233161787703297L},
            {0L, 2938735877055718L, -4244192909694994819L, -7908320945662590977L},
            {0L, 293873587705571L, -2269093698340454644L, -8169529724050079745L},
            {0L, 29387358770557L, 3462439444907864858L, -2661627379775963137L},
            {0L, 2938735877055L, -5187779277622078999L, 1578511669393358847L},
            {0L, 293873587705L, -7897475557246028547L, -5376172055173529601L},
            {0L, 29387358770L, -8168445185208423502L, -537617205517352961L},
            {0L, 2938735877L, 1027829888850112811L, 9169610316303040511L},
            {0L, 293873587L, -5431240233227854204L, 6450984253743169535L},
            {0L, 29387358L, -4232472838064695744L, 8023796054858137599L},
            {0L, 2938735L, -2267921691177424736L, 802379605485813759L},
            {0L, 293873L, -7605489798601563120L, 80237960548581375L},
            {0L, 29387L, 6618148649623664334L, 7386721425538678783L},
            {0L, 2938L, -4872208357150499052L, -6640025486929952769L},
            {0L, 293L, -2331895243086005067L, 4870020673419870207L},
            {0L, 29L, 7145508105175220139L, -5047021154770878465L},
            {0L, 2L, -1130123596853433148L, 6873995514006732799L},
            {0L, 0L, 5421010862427522170L, 687399551400673279L},
            {0L, 0L, 542101086242752217L, 68739955140067327L},
            {0L, 0L, 54210108624275221L, -5527149226598858753L},
            {0L, 0L, 5421010862427522L, 3136633892082024447L},
            {0L, 0L, 542101086242752L, 4003012203950112767L},
            {0L, 0L, 54210108624275L, 4089650035136921599L},
            {0L, 0L, 5421010862427L, -8814407033341083649L},
            {0L, 0L, 542101086242L, -4570789518076018689L},
            {0L, 0L, 54210108624L, 5076944270305263615L},
            {0L, 0L, 5421010862L, 7886392056514347007L},
            {0L, 0L, 542101086L, 4477988020393345023L},
            {0L, 0L, 54210108L, -6930898827444486145L},
            {0L, 0L, 5421010L, -2537764290115403777L},
            {0L, 0L, 542101L, 1590897978359414783L},
            {0L, 0L, 54210L, 2003764205206896639L},
            {0L, 0L, 5421L, 200376420520689663L},
            {0L, 0L, 542L, 1864712049423024127L},
            {0L, 0L, 54L, 3875820019684212735L},
            {0L, 0L, 5L, 7766279631452241919L},
            {0L, 0L, 0L, -8446744073709551617L},
            {0L, 0L, 0L, 999999999999999999L},
            {0L, 0L, 0L, 99999999999999999L},
            {0L, 0L, 0L, 9999999999999999L},
            {0L, 0L, 0L, 999999999999999L},
            {0L, 0L, 0L, 99999999999999L},
            {0L, 0L, 0L, 9999999999999L},
            {0L, 0L, 0L, 999999999999L},
            {0L, 0L, 0L, 99999999999L},
            {0L, 0L, 0L, 9999999999L},
            {0L, 0L, 0L, 999999999L},
            {0L, 0L, 0L, 99999999L},
            {0L, 0L, 0L, 9999999L},
            {0L, 0L, 0L, 999999L},
            {0L, 0L, 0L, 99999L},
            {0L, 0L, 0L, 9999L},
            {0L, 0L, 0L, 999L},
            {0L, 0L, 0L, 99L},
            {0L, 0L, 0L, 9L}
    };
    private static final long[] PRECISION_NEGATIVE_THRESHOLD_HH = {0L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -2L, -16L, -160L, -1594L, -15931L, -159310L, -1593092L, -15930920L, -159309192L, -1593091912L, -15930919112L, -159309191114L, -1593091911133L, -15930919111325L, -159309191113246L, -1593091911132453L, -15930919111324523L, -159309191113245228L, -1593091911132452278L,};
    private static final long[] PRECISION_NEGATIVE_THRESHOLD_HL = {0L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -3L, -30L, -294L, -2939L, -29388L, -293874L, -2938736L, -29387359L, -293873588L, -2938735878L, -29387358771L, -293873587706L, -2938735877056L, -29387358770558L, -293873587705572L, -2938735877055719L, -29387358770557188L, -293873587705571877L, -2938735877055718770L, 7506129376861915532L, 1274317473780948863L, -5703569335900062978L, -1695461137871974931L, 1492132694989802311L, -3525417123811528498L, 1639316909303818258L, -2053574980671369031L, -2089005733004138688L, -2443313256331835255L, -5986388489608800930L, -4523652674959354448L, -8343038602174441245L, 8803334346803345638L, -4200376900514301695L, -5110280857723913710L, 4237423643889517748L, 5480748291476074253L, -532749306367912314L,};
    private static final long[] PRECISION_NEGATIVE_THRESHOLD_LH = {0L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -6L, -55L, -543L, -5422L, -54211L, -542102L, -5421011L, -54210109L, -542101087L, -5421010863L, -54210108625L, -542101086243L, -5421010862428L, -54210108624276L, -542101086242753L, -5421010862427523L, -54210108624275222L, -542101086242752218L, -5421010862427522171L, 1130123596853433147L, -7145508105175220140L, 2331895243086005066L, 4872208357150499051L, -6618148649623664335L, 7605489798601563119L, 2267921691177424735L, 4232472838064695743L, 5431240233227854203L, -1027829888850112812L, 8168445185208423501L, 7897475557246028546L, 5187779277622078998L, -3462439444907864859L, 2269093698340454643L, 4244192909694994818L, 5548440949530844952L, 144177274179794674L, 1441772741797946748L, -4029016655730084129L, -3396678409881738057L, 2926704048601722662L, -7626447661401876603L, -2477500319180559563L, -6328259118096044007L, -7942358959831785217L, -5636613303479645706L, -1025900813667802212L, 8187735937031529496L, 8090383075477088496L, 7116854459932678496L, -2618431695511421504L, -7737572881404663424L, -3588752519208427776L, 1005962955334825472L, -8387114520361296896L, 8362575164934789120L, -8607968719199866880L,};
    private static final long[] PRECISION_NEGATIVE_THRESHOLD_LL = {0L, -9L, -99L, -999L, -9999L, -99999L, -999999L, -9999999L, -99999999L, -999999999L, -9999999999L, -99999999999L, -999999999999L, -9999999999999L, -99999999999999L, -999999999999999L, -9999999999999999L, -99999999999999999L, -999999999999999999L, 8446744073709551617L, -7766279631452241919L, -3875820019684212735L, -1864712049423024127L, -200376420520689663L, -2003764205206896639L, -1590897978359414783L, 2537764290115403777L, 6930898827444486145L, -4477988020393345023L, -7886392056514347007L, -5076944270305263615L, 4570789518076018689L, 8814407033341083649L, -4089650035136921599L, -4003012203950112767L, -3136633892082024447L, 5527149226598858753L, -68739955140067327L, -687399551400673279L, -6873995514006732799L, 5047021154770878465L, -4870020673419870207L, 6640025486929952769L, -7386721425538678783L, -80237960548581375L, -802379605485813759L, -8023796054858137599L, -6450984253743169535L, -9169610316303040511L, 537617205517352961L, 5376172055173529601L, -1578511669393358847L, 2661627379775963137L, 8169529724050079745L, 7908320945662590977L, 5296233161787703297L, -2377900603251621887L, -5332261958806667263L, 2017612633061982209L, 1729382256910270465L, -1152921504606846975L, 6917529027641081857L, -4611686018427387903L, -9223372036854775807L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,};
    private static final long[] PRECISION_POSITIVE_THRESHOLD_HH = {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 15L, 159L, 1593L, 15930L, 159309L, 1593091L, 15930919L, 159309191L, 1593091911L, 15930919111L, 159309191113L, 1593091911132L, 15930919111324L, 159309191113245L, 1593091911132452L, 15930919111324522L, 159309191113245227L, 1593091911132452277L,};
    private static final long[] PRECISION_POSITIVE_THRESHOLD_HL = {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 2L, 29L, 293L, 2938L, 29387L, 293873L, 2938735L, 29387358L, 293873587L, 2938735877L, 29387358770L, 293873587705L, 2938735877055L, 29387358770557L, 293873587705571L, 2938735877055718L, 29387358770557187L, 293873587705571876L, 2938735877055718769L, -7506129376861915533L, -1274317473780948864L, 5703569335900062977L, 1695461137871974930L, -1492132694989802312L, 3525417123811528497L, -1639316909303818259L, 2053574980671369030L, 2089005733004138687L, 2443313256331835254L, 5986388489608800929L, 4523652674959354447L, 8343038602174441244L, -8803334346803345639L, 4200376900514301694L, 5110280857723913709L, -4237423643889517749L, -5480748291476074254L, 532749306367912313L,};
    private static final long[] PRECISION_POSITIVE_THRESHOLD_LH = {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 5L, 54L, 542L, 5421L, 54210L, 542101L, 5421010L, 54210108L, 542101086L, 5421010862L, 54210108624L, 542101086242L, 5421010862427L, 54210108624275L, 542101086242752L, 5421010862427522L, 54210108624275221L, 542101086242752217L, 5421010862427522170L, -1130123596853433148L, 7145508105175220139L, -2331895243086005067L, -4872208357150499052L, 6618148649623664334L, -7605489798601563120L, -2267921691177424736L, -4232472838064695744L, -5431240233227854204L, 1027829888850112811L, -8168445185208423502L, -7897475557246028547L, -5187779277622078999L, 3462439444907864858L, -2269093698340454644L, -4244192909694994819L, -5548440949530844953L, -144177274179794675L, -1441772741797946749L, 4029016655730084128L, 3396678409881738056L, -2926704048601722663L, 7626447661401876602L, 2477500319180559562L, 6328259118096044006L, 7942358959831785216L, 5636613303479645705L, 1025900813667802211L, -8187735937031529497L, -8090383075477088497L, -7116854459932678497L, 2618431695511421503L, 7737572881404663423L, 3588752519208427775L, -1005962955334825473L, 8387114520361296895L, -8362575164934789121L, 8607968719199866879L,};
    // Positive precision thresholds, gives the maximum value for a specific
    // precision.
    private static final long[] PRECISION_POSITIVE_THRESHOLD_LL = {0L, 9L, 99L, 999L, 9999L, 99999L, 999999L, 9999999L, 99999999L, 999999999L, 9999999999L, 99999999999L, 999999999999L, 9999999999999L, 99999999999999L, 999999999999999L, 9999999999999999L, 99999999999999999L, 999999999999999999L, -8446744073709551617L, 7766279631452241919L, 3875820019684212735L, 1864712049423024127L, 200376420520689663L, 2003764205206896639L, 1590897978359414783L, -2537764290115403777L, -6930898827444486145L, 4477988020393345023L, 7886392056514347007L, 5076944270305263615L, -4570789518076018689L, -8814407033341083649L, 4089650035136921599L, 4003012203950112767L, 3136633892082024447L, -5527149226598858753L, 68739955140067327L, 687399551400673279L, 6873995514006732799L, -5047021154770878465L, 4870020673419870207L, -6640025486929952769L, 7386721425538678783L, 80237960548581375L, 802379605485813759L, 8023796054858137599L, 6450984253743169535L, 9169610316303040511L, -537617205517352961L, -5376172055173529601L, 1578511669393358847L, -2661627379775963137L, -8169529724050079745L, -7908320945662590977L, -5296233161787703297L, 2377900603251621887L, 5332261958806667263L, -2017612633061982209L, -1729382256910270465L, 1152921504606846975L, -6917529027641081857L, 4611686018427387903L, 9223372036854775807L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L, -1L,};
    private static final BigDecimal[] ZERO_SCALED = new BigDecimal[32];
    private final DecimalKnuthDivider divider = new DecimalKnuthDivider();
    private long hh;    // Highest 64 bits (bits 192-255)
    private long hl;    // High 64 bits (bits 128-191)
    private long lh;    // Mid 64 bits (bits 64-127)
    private long ll;    // Low 64 bits (bits 0-63)
    private int scale;  // Number of decimal places
    // @formatter:on

    /**
     * Default constructor - creates zero with scale 0
     */
    public Decimal256() {
        this.hh = 0;
        this.hl = 0;
        this.lh = 0;
        this.ll = 0;
        this.scale = 0;
    }

    /**
     * Constructor with initial values.
     *
     * @param hh    the highest 64 bits of the decimal value (bits 192-255)
     * @param hl    the high 64 bits of the decimal value (bits 128-191)
     * @param lh    the mid 64 bits of the decimal value (bits 64-127)
     * @param ll    the low 64 bits of the decimal value (bits 0-63)
     * @param scale the number of decimal places
     * @throws NumericException if scale is invalid
     */
    public Decimal256(long hh, long hl, long lh, long ll, int scale) {
        validateScale(scale);
        this.hh = hh;
        this.hl = hl;
        this.lh = lh;
        this.ll = ll;
        this.scale = scale;
    }

    /**
     * Static addition method.
     *
     * @param a      the first operand
     * @param b      the second operand
     * @param result the result (can be the same as a or b for in-place operation)
     * @throws NumericException if overflow occurs
     */
    public static void add(Decimal256 a, Decimal256 b, Decimal256 result) {
        result.copyFrom(a);
        result.add(b);
    }

    /**
     * Compares 2 Decimal256, ignoring scaling.
     */
    public static int compare(long aHH, long aHL, long aLH, long aLL, long bHH, long bHL, long bLH, long bLL) {
        int s = Long.compare(aHH, bHH);
        if (s != 0) {
            return s;
        }
        s = Long.compareUnsigned(aHL, bHL);
        if (s != 0) {
            return s;
        }
        s = Long.compareUnsigned(aLH, bLH);
        if (s != 0) {
            return s;
        }
        return Long.compareUnsigned(aLL, bLL);
    }

    public static int compare(Decimal256 a, Decimal256 b) {
        int s = Long.compare(a.getHh(), b.getHh());
        if (s != 0) {
            return s;
        }
        s = Long.compareUnsigned(a.getHl(), b.getHl());
        if (s != 0) {
            return s;
        }
        s = Long.compareUnsigned(a.getLh(), b.getLh());
        if (s != 0) {
            return s;
        }
        return Long.compareUnsigned(a.getLl(), b.getLl());
    }

    public static int compare(Decimal256 a, long bHH, long bHL, long bLH, long bLL) {
        return compare(
                a.getHh(), a.getHl(), a.getLh(), a.getLl(),
                bHH, bHL, bLH, bLL
        );
    }

    public static int compare(long aHH, long aHL, long aLH, long aLL, Decimal256 b) {
        return compare(
                aHH, aHL, aLH, aLL,
                b.getHh(), b.getHl(), b.getLh(), b.getLl()
        );
    }

    /**
     * Compare two Decimal256.
     *
     * @return -1, 0, or 1 if a is less than, equal to, or greater than b
     */
    public static int compareTo(long aHH, long aHL, long aLH, long aLL, int aScale, long bHH, long bHL, long bLH, long bLL, int bScale) {
        if (isNull(aHH, aHL, aLH, aLL)) {
            if (isNull(bHH, bHL, bLH, bLL)) {
                return 0;
            }
            return -1;
        }
        if (isNull(bHH, bHL, bLH, bLL)) {
            return 1;
        }

        boolean aNeg = aHH < 0;
        boolean bNeg = bHH < 0;
        if (aNeg != bNeg) {
            return aNeg ? -1 : 1;
        }

        if (aScale == bScale) {
            // Same scale - direct comparison
            return compare(aHH, aHL, aLH, aLL, bHH, bHL, bLH, bLL);
        }

        if (aNeg) {
            aLL = ~aLL + 1;
            long c = aLL == 0L ? 1L : 0L;
            aLH = ~aLH + c;
            c = (c == 1L && aLH == 0L) ? 1L : 0L;
            aHL = ~aHL + c;
            c = (c == 1L && aHL == 0L) ? 1L : 0L;
            aHH = ~aHH + c;

            // Negate b
            bLL = ~bLL + 1;
            c = bLL == 0L ? 1L : 0L;
            bLH = ~bLH + c;
            c = (c == 1L && bLH == 0L) ? 1L : 0L;
            bHL = ~bHL + c;
            c = (c == 1L && bHL == 0L) ? 1L : 0L;
            bHH = ~bHH + c;
        }

        Decimal256 holder = Misc.getThreadLocalDecimal256();
        if (aScale < bScale) {
            holder.of(aHH, aHL, aLH, aLL, aScale);
            holder.multiplyByPowerOf10InPlace(bScale - aScale);
            aHH = holder.hh;
            aHL = holder.hl;
            aLH = holder.lh;
            aLL = holder.ll;
        } else {
            holder.of(bHH, bHL, bLH, bLL, bScale);
            holder.multiplyByPowerOf10InPlace(aScale - bScale);
            bHH = holder.hh;
            bHL = holder.hl;
            bLH = holder.lh;
            bLL = holder.ll;
        }

        return compare(aHH, aHL, aLH, aLL, bHH, bHL, bLH, bLL) * (aNeg ? -1 : 1);
    }

    /**
     * Static division method.
     *
     * @param dividend     the dividend
     * @param divisor      the divisor
     * @param result       the result
     * @param scale        the desired scale of the result
     * @param roundingMode the rounding mode
     * @throws NumericException if division by zero or overflow occurs
     */
    public static void divide(Decimal256 dividend, Decimal256 divisor, Decimal256 result, int scale, RoundingMode roundingMode) {
        divide(
                result.divider,
                dividend.hh, dividend.hl, dividend.lh, dividend.ll, dividend.scale,
                divisor.hh, divisor.hl, divisor.lh, divisor.ll, divisor.scale,
                result, scale, roundingMode
        );
    }

    /**
     * Static division method using raw 256-bit values.
     *
     * @param dividendHH    the dividend's highest 64 bits
     * @param dividendHL    the dividend's high 64 bits
     * @param dividendLH    the dividend's mid 64 bits
     * @param dividendLL    the dividend's low 64 bits
     * @param dividendScale the dividend's scale
     * @param divisorHH     the divisor's highest 64 bits
     * @param divisorHL     the divisor's high 64 bits
     * @param divisorLH     the divisor's mid 64 bits
     * @param divisorLL     the divisor's low 64 bits
     * @param divisorScale  the divisor's scale
     * @param result        the result Decimal256 to store the quotient
     * @param scale         the desired scale of the result
     * @param roundingMode  the rounding mode
     * @throws NumericException if division by zero or overflow occurs
     */
    public static void divide(
            DecimalKnuthDivider divider,
            long dividendHH,
            long dividendHL,
            long dividendLH,
            long dividendLL,
            int dividendScale,
            long divisorHH,
            long divisorHL,
            long divisorLH,
            long divisorLL,
            int divisorScale,
            Decimal256 result,
            int scale,
            RoundingMode roundingMode
    ) {
        if (isNull(dividendHH, dividendHL, dividendLH, dividendLL) || isNull(divisorHH, divisorHL, divisorLH, divisorLL)) {
            result.ofNull();
            return;
        }

        validateScale(scale);

        // Compute the delta: how much power of 10 we should raise either the dividend or divisor.
        int delta = scale + (divisorScale - dividendScale);

        // Fail early if we're sure to overflow.
        if ((delta > 0 && (dividendScale + delta) >= MAX_SCALE) || (delta < 0 && (divisorScale - delta) >= MAX_SCALE)) {
            throw NumericException.instance().put("Overflow in division: resulting scale would exceed maximum (" + MAX_SCALE + ")");
        }

        final boolean isNegative = (dividendHH < 0) ^ (divisorHH < 0);

        // We need to have both dividend and divisor positive for scaling and division.
        if (dividendHH < 0) {
            dividendLL = ~dividendLL + 1;
            long c = dividendLL == 0L ? 1L : 0L;
            dividendLH = ~dividendLH + c;
            c = (c == 1L && dividendLH == 0L) ? 1L : 0L;
            dividendHL = ~dividendHL + c;
            c = (c == 1L && dividendHL == 0L) ? 1L : 0L;
            dividendHH = ~dividendHH + c;
        }

        if (divisorHH < 0) {
            divisorLL = ~divisorLL + 1;
            long c = divisorLL == 0L ? 1L : 0L;
            divisorLH = ~divisorLH + c;
            c = (c == 1L && divisorLH == 0L) ? 1L : 0L;
            divisorHL = ~divisorHL + c;
            c = (c == 1L && divisorHL == 0L) ? 1L : 0L;
            divisorHH = ~divisorHH + c;
        }

        if (delta > 0) {
            // We need to raise the dividend to 10^delta
            result.of(dividendHH, dividendHL, dividendLH, dividendLL, scale);
            result.multiplyByPowerOf10InPlace(delta);
            dividendHH = result.hh;
            dividendHL = result.hl;
            dividendLH = result.lh;
            dividendLL = result.ll;
        } else if (delta < 0) {
            result.of(divisorHH, divisorHL, divisorLH, divisorLL, scale);
            result.multiplyByPowerOf10InPlace(-delta);
            divisorHH = result.hh;
            divisorHL = result.hl;
            divisorLH = result.lh;
            divisorLL = result.ll;
        }

        divider.clear();
        divider.ofDividend(dividendHH, dividendHL, dividendLH, dividendLL);
        divider.ofDivisor(divisorHH, divisorHL, divisorLH, divisorLL);
        divider.divide(isNegative, roundingMode);
        divider.sink(result, scale);

        if (isNegative) {
            result.negate();
        }
    }

    /**
     * Create Decimal256 from a BigDecimal.
     *
     * @param bd the BigDecimal value
     * @return new Decimal256 instance
     * @throws NumericException if the value cannot be represented in 256 bits
     */
    public static Decimal256 fromBigDecimal(BigDecimal bd) {
        if (bd == null) {
            throw NumericException.instance().put("BigDecimal cannot be null");
        }

        BigInteger unscaledValue = bd.unscaledValue();
        int scale = bd.scale();

        if (scale < 0) {
            // We don't support negative scale, we must transform the value to match
            // our format.
            unscaledValue = unscaledValue.multiply(new BigInteger("10").pow(-scale));
            scale = 0;
        }

        // Check if the value fits in 256 bits
        if (unscaledValue.bitLength() > 255) {
            throw NumericException.instance().put("BigDecimal value too large for Decimal256");
        }

        boolean negative = unscaledValue.signum() == -1;
        if (negative) {
            unscaledValue = unscaledValue.negate();
        }

        // Convert to 256-bit representation
        byte[] bytes = unscaledValue.toByteArray();
        Decimal256 result = new Decimal256();
        result.scale = scale;

        // Fill the 256-bit value from the byte array
        result.setFromByteArray(bytes);
        validateScale(scale);

        if (negative) {
            result.negate();
        }

        return result;
    }

    /**
     * Create Decimal256 from a double value with specified scale.
     *
     * @param value the double value
     * @param scale the desired scale
     * @return new Decimal256 instance
     * @throws NumericException if scale is invalid
     */
    public static Decimal256 fromDouble(double value, int scale) {
        validateScale(scale);
        BigDecimal bd = new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP);
        return fromBigDecimal(bd);
    }

    /**
     * Create Decimal256 from a long value with specified scale.
     *
     * @param value the long value
     * @param scale the desired scale
     * @return new Decimal256 instance
     * @throws NumericException if scale is invalid
     */
    public static Decimal256 fromLong(long value, int scale) {
        validateScale(scale);

        Decimal256 result = new Decimal256();
        result.ofLong(value, scale);
        return result;
    }

    /**
     * Extracts the digit at a specific power-of-ten position from a 256-bit decimal number.
     * <p>
     * Uses binary search to efficiently determine which digit (0-9) should appear at the
     * given power-of-ten position when the decimal is represented in base 10.
     * <p>
     * Prerequisites:
     * - The decimal value must be positive
     * - The decimal value must be less than 10^pow (e.g., for pow=3, decimal must be &lt; 1000)
     *
     * @param hh  highest 64 bits of the 256-bit decimal
     * @param hl  high-middle 64 bits of the 256-bit decimal
     * @param lh  low-middle 64 bits of the 256-bit decimal
     * @param ll  lowest 64 bits of the 256-bit decimal
     * @param pow the power of ten position to extract (0 = ones place, 1 = tens place, etc.)
     * @return the digit (0-9) at the specified power-of-ten position
     */
    public static int getDigitAtPowerOfTen(long hh, long hl, long lh, long ll, int pow) {
        // We do a binary search to retrieve the digit we need to display at a specific power
        if (compareToPowerOfTen(hh, hl, lh, ll, pow, 5) >= 0) {
            if (compareToPowerOfTen(hh, hl, lh, ll, pow, 7) >= 0) {
                if (compareToPowerOfTen(hh, hl, lh, ll, pow, 9) >= 0) {
                    return 9;
                } else if (compareToPowerOfTen(hh, hl, lh, ll, pow, 8) >= 0) {
                    return 8;
                } else {
                    return 7;
                }
            } else {
                if (compareToPowerOfTen(hh, hl, lh, ll, pow, 6) >= 0) {
                    return 6;
                } else {
                    return 5;
                }
            }
        } else {
            if (compareToPowerOfTen(hh, hl, lh, ll, pow, 3) >= 0) {
                if (compareToPowerOfTen(hh, hl, lh, ll, pow, 4) >= 0) {
                    return 4;
                } else {
                    return 3;
                }
            } else {
                if (compareToPowerOfTen(hh, hl, lh, ll, pow, 2) >= 0) {
                    return 2;
                } else if (compareToPowerOfTen(hh, hl, lh, ll, pow, 1) >= 0) {
                    return 1;
                }
            }
        }
        return 0;
    }

    @TestOnly
    public static long[][] getPowersTenTable() {
        return POWERS_TEN_TABLE;
    }

    @TestOnly
    public static long[][] getPowersTenThresholdsTable() {
        return POWERS_TEN_TABLE_THRESHOLDS;
    }

    /**
     * Check if addition resulted in a carry
     * When adding two unsigned numbers a + b = sum, carry occurs iff sum &lt; a (or sum &lt; b)
     * This works because:
     * - No carry: sum = a + b, so sum &gt;= a and sum &gt;= b
     * - Carry: sum = a + b - 2^64, so sum &lt; a and sum &lt; b
     */
    public static boolean hasCarry(long a, long sum) {
        // We can check against either a or b - both work
        // Using a for consistency, b parameter kept for clarity
        return (sum + Long.MIN_VALUE) < (a + Long.MIN_VALUE);
    }

    /**
     * Returns whether the Decimal256 is null or not.
     *
     * @param hh the highest 64 bits (bits 192-255)
     * @param hl the high 64 bits (bits 128-191)
     * @param lh the mid 64 bits (bits 64-127)
     * @param ll the low 64 bits (bits 0-63)
     * @return true if null, false otherwise
     */
    public static boolean isNull(long hh, long hl, long lh, long ll) {
        return hh == Decimals.DECIMAL256_HH_NULL && hl == Decimals.DECIMAL256_HL_NULL
                && lh == Decimals.DECIMAL256_LH_NULL && ll == Decimals.DECIMAL256_LL_NULL;
    }

    /**
     * Static modulo method.
     *
     * @param dividend the dividend
     * @param divisor  the divisor
     * @param result   the result
     * @throws NumericException if division by zero occurs
     */
    public static void modulo(Decimal256 dividend, Decimal256 divisor, Decimal256 result) {
        result.copyFrom(dividend);
        result.modulo(divisor);
    }

    /**
     * Static multiplication method.
     *
     * @param a      the first operand
     * @param b      the second operand
     * @param result the result (can be the same as a or b for in-place operation)
     * @throws NumericException if overflow occurs
     */
    public static void multiply(Decimal256 a, Decimal256 b, Decimal256 result) {
        result.copyFrom(a);
        result.multiply(b);
    }

    public static void put(Decimal256 d, long addr) {
        put(d.getHh(), d.getHl(), d.getLh(), d.getLl(), addr);
    }

    public static void put(long hh, long hl, long lh, long ll, long addr) {
        Unsafe.getUnsafe().putLong(addr, hh);
        Unsafe.getUnsafe().putLong(addr + Long.BYTES, hl);
        Unsafe.getUnsafe().putLong(addr + Long.BYTES * 2, lh);
        Unsafe.getUnsafe().putLong(addr + Long.BYTES * 3, ll);
    }

    public static void putNull(long addr) {
        Unsafe.getUnsafe().putLong(addr, Decimals.DECIMAL256_HH_NULL);
        Unsafe.getUnsafe().putLong(addr + 8L, Decimals.DECIMAL256_HL_NULL);
        Unsafe.getUnsafe().putLong(addr + 16L, Decimals.DECIMAL256_LH_NULL);
        Unsafe.getUnsafe().putLong(addr + 24L, Decimals.DECIMAL256_LL_NULL);
    }

    /**
     * Static subtraction method.
     *
     * @param a      the first operand
     * @param b      the second operand
     * @param result the result (can be the same as a or b for in-place operation)
     * @throws NumericException if overflow occurs
     */
    public static void subtract(Decimal256 a, Decimal256 b, Decimal256 result) {
        result.copyFrom(a);
        result.subtract(b);
    }

    public static @NotNull BigDecimal toBigDecimal(long hh, long hl, long lh, long ll, int scale) {
        // Convert 256-bit value to BigInteger
        byte[] bytes = new byte[32]; // 256 bits = 32 bytes
        // Fill bytes in big-endian order
        putLongIntoBytes(bytes, 0, hh);
        putLongIntoBytes(bytes, 8, hl);
        putLongIntoBytes(bytes, 16, lh);
        putLongIntoBytes(bytes, 24, ll);

        BigInteger unscaledValue = new BigInteger(bytes);
        return new BigDecimal(unscaledValue, scale);
    }

    /**
     * Writes the string representation of the given Decimal256 to the specified CharSink.
     * The output format is a plain decimal string without scientific notation.
     *
     * @param sink the CharSink to write to
     */
    public static void toSink(@NotNull CharSink<?> sink, long hh, long hl, long lh, long ll, int scale) {
        toSink(sink, hh, hl, lh, ll, scale, MAX_SCALE);
    }

    /**
     * Writes the string representation of the given Decimal256 to the specified CharSink.
     * The output format is a plain decimal string without scientific notation.
     *
     * @param sink the CharSink to write to
     */
    public static void toSink(@NotNull CharSink<?> sink, long hh, long hl, long lh, long ll, int scale, int precision) {
        if (isNull(hh, hl, lh, ll)) {
            return;
        }

        if (hh < 0) {
            ll = ~ll + 1;
            long c = ll == 0L ? 1L : 0L;
            lh = ~lh + c;
            c = (c == 1L && lh == 0L) ? 1L : 0L;
            hl = ~hl + c;
            c = (c == 1L && hl == 0L) ? 1L : 0L;
            hh = ~hh + c;
            sink.put('-');
        }

        boolean printed = false;
        for (int i = precision - 1; i >= 0; i--) {
            if (i == scale - 1) {
                if (!printed) {
                    sink.put('0');
                }
                printed = true;
                sink.put('.');
            }

            // Fast path, we expect most digits to be 0
            if (compareToPowerOfTen(hh, hl, lh, ll, i, 1) < 0) {
                if (printed) {
                    sink.put('0');
                }
                continue;
            }

            int mul = getDigitAtPowerOfTen(hh, hl, lh, ll, i);
            sink.putAscii((char) ('0' + mul));
            printed = true;

            // Subtract the value and continue again
            int offset = (mul - 1) * 4;

            long bLL = ~POWERS_TEN_TABLE[i][offset + 3] + 1;
            long c = bLL == 0L ? 1L : 0L;
            long r = ll + bLL;
            long carry = hasCarry(ll, r) ? 1L : 0L;
            ll = r;

            long bLH = ~POWERS_TEN_TABLE[i][offset + 2] + c;
            c = (c == 1L && bLH == 0L) ? 1L : 0L;
            long t = lh + carry;
            carry = hasCarry(lh, t) ? 1L : 0L;
            r = t + bLH;
            carry |= hasCarry(t, r) ? 1L : 0L;
            lh = r;

            long bHL = ~POWERS_TEN_TABLE[i][offset + 1] + c;
            c = (c == 1L && bHL == 0L) ? 1L : 0L;
            t = hl + carry;
            carry = hasCarry(hl, t) ? 1L : 0L;
            r = t + bHL;
            carry |= hasCarry(t, r) ? 1L : 0L;
            hl = r;

            long bHH = ~POWERS_TEN_TABLE[i][offset] + c;
            hh += carry + bHH;
        }

        if (!printed) {
            sink.put('0');
        }
    }

    /**
     * Adds the two's-complement representation of {@link Decimal128} {@code b} to {@code result} in place.
     * <p>
     * The 128-bit operand is sign-extended to 256 bits before performing the addition. This helper assumes
     * that both operands already share the same scale and that the resulting value fits within a {@link Decimal256};
     * callers must enforce those preconditions because no overflow or scale validation is performed here.
     *
     * @param result the accumulator that receives the sum (updated in place)
     * @param b      the 128-bit value to add, interpreted using two's-complement semantics
     */
    public static void uncheckedAdd(Decimal256 result, Decimal128 b) {
        long sign = b.getHigh() < 0 ? -1L : 0L;
        long r = result.ll + b.getLow();
        long carry = hasCarry(result.ll, r) ? 1L : 0L;
        result.ll = r;

        long t = result.lh + carry;
        carry = hasCarry(result.lh, t) ? 1L : 0L;
        r = t + b.getHigh();
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.lh = r;

        t = result.hl + carry;
        carry = hasCarry(result.hl, t) ? 1L : 0L;
        r = t + sign;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.hl = r;
        result.hh = result.hh + carry + sign;
    }

    /**
     * Adds the two's-complement representation of {@link Decimal64} {@code b} to {@code result} in place.
     * <p>
     * The 64-bit operand is sign-extended to 256 bits before performing the addition. This helper assumes
     * that both operands already share the same scale and that the resulting value fits within a {@link Decimal256};
     * callers must enforce those preconditions because no overflow or scale validation is performed here.
     *
     * @param result the accumulator that receives the sum (updated in place)
     * @param b      the 64-bit value to add, interpreted using two's-complement semantics
     */
    public static void uncheckedAdd(Decimal256 result, long b) {
        long sign = b < 0 ? -1L : 0L;
        long r = result.ll + b;
        long carry = hasCarry(result.ll, r) ? 1L : 0L;
        result.ll = r;

        long t = result.lh + carry;
        carry = hasCarry(result.lh, t) ? 1L : 0L;
        r = t + sign;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.lh = r;

        t = result.hl + carry;
        carry = hasCarry(result.hl, t) ? 1L : 0L;
        r = t + sign;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.hl = r;
        result.hh = result.hh + carry + sign;
    }

    public static void uncheckedAdd(Decimal256 result, Decimal256 b) {
        uncheckedAdd(result, b.hh, b.hl, b.lh, b.ll);
    }

    /**
     * In-place addition.
     *
     * @throws NumericException if overflow occurs
     */
    public void add(long otherHH, long otherHL, long otherLH, long otherLL, int otherScale) {
        if (isNull()) {
            return;
        }

        if (isNull(otherHH, otherHL, otherLH, otherLL)) {
            ofNull();
            return;
        }

        add(this, hh, hl, lh, ll, scale, otherHH, otherHL, otherLH, otherLL, otherScale);
    }

    /**
     * In-place addition.
     *
     * @param other the Decimal256 to add
     * @throws NumericException if overflow occurs
     */
    public void add(Decimal256 other) {
        add(other.hh, other.hl, other.lh, other.ll, other.scale);
    }

    /**
     * Add a specific multiplier of a power of ten to the current 256-bit value.
     * This method modifies the value in-place by adding (multiplier * 10^pow).
     *
     * @param pow        the power of ten position
     * @param multiplier the digit to add (1-9, or 0 for no-op)
     */
    public void addPowerOfTenMultiple(int pow, int multiplier) {
        if (multiplier == 0 || multiplier > 9) {
            return;
        }

        final int offset = (multiplier - 1) * 4;
        final long bHH = Decimal256.POWERS_TEN_TABLE[pow][offset];
        final long bHL = Decimal256.POWERS_TEN_TABLE[pow][offset + 1];
        final long bLH = Decimal256.POWERS_TEN_TABLE[pow][offset + 2];
        final long bLL = Decimal256.POWERS_TEN_TABLE[pow][offset + 3];
        uncheckedAdd(this, bHH, bHL, bLH, bLL);
    }

    /**
     * Compare the decimal precision with the other.
     *
     * @return true if the decimal precision is smaller or equal to the given precision, false otherwise.
     */
    public boolean comparePrecision(int precision) {
        if (isNull()) {
            return true;
        }

        if (precision > MAX_SCALE) {
            return true;
        } else if (precision < 0) {
            return false;
        }

        if (hh >= 0) {
            final long thresholdHH = PRECISION_POSITIVE_THRESHOLD_HH[precision];
            final long thresholdHL = PRECISION_POSITIVE_THRESHOLD_HL[precision];
            final long thresholdLH = PRECISION_POSITIVE_THRESHOLD_LH[precision];
            final long thresholdLL = PRECISION_POSITIVE_THRESHOLD_LL[precision];
            return compareTo0(thresholdHH, thresholdHL, thresholdLH, thresholdLL) <= 0;
        } else {
            final long thresholdHH = PRECISION_NEGATIVE_THRESHOLD_HH[precision];
            final long thresholdHL = PRECISION_NEGATIVE_THRESHOLD_HL[precision];
            final long thresholdLH = PRECISION_NEGATIVE_THRESHOLD_LH[precision];
            final long thresholdLL = PRECISION_NEGATIVE_THRESHOLD_LL[precision];
            return compareTo0(thresholdHH, thresholdHL, thresholdLH, thresholdLL) >= 0;
        }
    }

    /**
     * Compare this Decimal256 with another.
     *
     * @param other the Decimal256 to compare with
     * @return -1, 0, or 1 as this is less than, equal to, or greater than other
     */
    public int compareTo(Decimal256 other) {
        return compareTo(other.hh, other.hl, other.lh, other.ll, other.scale);
    }

    /**
     * Compare this Decimal256 with another.
     *
     * @return -1, 0, or 1 if this is less than, equal to, or greater than other
     */
    public int compareTo(long otherHH, long otherHL, long otherLH, long otherLL, int otherScale) {
        return compareTo(hh, hl, lh, ll, scale, otherHH, otherHL, otherLH, otherLL, otherScale);
    }

    /**
     * Copy values from another Decimal256 instance.
     *
     * @param other the Decimal256 instance to copy from
     */
    public void copyFrom(Decimal256 other) {
        this.hh = other.hh;
        this.hl = other.hl;
        this.lh = other.lh;
        this.ll = other.ll;
        this.scale = other.scale;
    }

    /**
     * Copy values from another Decimal256 instance without the scale.
     *
     * @param other the Decimal256 instance to copy from
     */
    public void copyRaw(Decimal256 other) {
        this.hh = other.hh;
        this.hl = other.hl;
        this.lh = other.lh;
        this.ll = other.ll;
    }

    /**
     * In-place division.
     *
     * @param divisor      the Decimal256 to divide by
     * @param targetScale  the desired scale of the result
     * @param roundingMode the rounding mode
     * @throws NumericException if division by zero or overflow occurs
     */
    public void divide(Decimal256 divisor, int targetScale, RoundingMode roundingMode) {
        divide(this, divisor, this, targetScale, roundingMode);
    }

    /**
     * In-place division.
     *
     * @param targetScale  the desired scale of the result
     * @param roundingMode the rounding mode
     * @throws NumericException if division by zero or overflow occurs
     */
    public void divide(long divisorHH, long divisorHL, long divisorLH, long divisorLL, int divisorScale, int targetScale, RoundingMode roundingMode) {
        divide(
                this.divider,
                this.hh, this.hl, this.lh, this.ll, this.scale,
                divisorHH, divisorHL, divisorLH, divisorLL, divisorScale,
                this, targetScale, roundingMode
        );
    }

    /**
     * Checks if this Decimal256 is equal to another object.
     * Two Decimal256 instances are equal if they have the same 256-bit value and scale.
     *
     * @param obj the object to compare with
     * @return true if equal, false otherwise
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Decimal256 other)) return false;

        final boolean isNull = isNull();
        final boolean otherIsNull = other.isNull();
        if (isNull != otherIsNull) {
            return false;
        } else if (isNull) {
            return true; // We don't need to compare scales for null values
        }
        return hh == other.hh &&
                hl == other.hl &&
                lh == other.lh &&
                ll == other.ll &&
                scale == other.scale;
    }

    /**
     * Checks if this Decimal256 value fits within the specified storage size (pow 2).
     * The value fits if its absolute magnitude can be represented with the given number of digits.
     *
     * @param size the target size (number of bytes available pow 2, e.g., 4 bytes -> 2)
     * @return true if the value fits within the storage size, false otherwise
     */
    public boolean fitsInStorageSizePow2(int size) {
        return switch (size) {
            case 0 -> // 1 byte - max magnitude 127
                    (hh == 0 || hh == -1) &&
                            (hl == 0 || hl == -1) &&
                            (lh == 0 || lh == -1) &&
                            Math.abs(ll) <= 0x7FL;
            case 1 -> // 2 bytes - max magnitude 32,767
                    (hh == 0 || hh == -1) &&
                            (hl == 0 || hl == -1) &&
                            (lh == 0 || lh == -1) &&
                            Math.abs(ll) <= 0x7FFFL;
            case 2 -> // 4 bytes - max magnitude 2,147,483,647
                    (hh == 0 || hh == -1) &&
                            (hl == 0 || hl == -1) &&
                            (lh == 0 || lh == -1) &&
                            Math.abs(ll) <= 0x7FFFFFFFL;
            case 3 -> // 8 bytes - max magnitude 9,223,372,036,854,775,807
                    (hh == 0 || hh == -1) &&
                            (hl == 0 || hl == -1) &&
                            (lh == 0 || lh == -1);
            // ll can use full long range

            case 4 -> // 128-bit storage
                    (hh == 0 || hh == -1) &&
                            (hl == 0 || hl == -1);
            // lh and ll can use full range

            case 5 -> // 256-bit storage
                    true; // Always fits in 256-bit

            default -> false;
        };
    }

    /**
     * Extracts the digit at a specific power-of-ten position from a 256-bit decimal number.
     * <p>
     * Uses binary search to efficiently determine which digit (0-9) should appear at the
     * given power-of-ten position when the decimal is represented in base 10.
     * <p>
     * Prerequisites:
     * - The decimal value must be positive
     * - The decimal value must be less than 10^pow (e.g., for pow=3, decimal must be &lt; 10,000)
     *
     * @param pow the power of ten position to extract (0 = ones place, 1 = tens place, etc.)
     * @return the digit (0-9) at the specified power-of-ten position
     */
    public int getDigitAtPowerOfTen(int pow) {
        return getDigitAtPowerOfTen(hh, hl, lh, ll, pow);
    }

    /**
     * Gets the highest 64 bits of the 256-bit decimal value (bits 192-255).
     *
     * @return the highest 64 bits
     */
    public long getHh() {
        return hh;
    }

    /**
     * Gets the high 64 bits of the 256-bit decimal value (bits 128-191).
     *
     * @return the high 64 bits
     */
    public long getHl() {
        return hl;
    }

    /**
     * Gets the mid 64 bits of the 256-bit decimal value (bits 64-127).
     *
     * @return the mid 64 bits
     */
    public long getLh() {
        return lh;
    }

    /**
     * Gets the low 64 bits of the 256-bit decimal value (bits 0-63).
     *
     * @return the low 64 bits
     */
    public long getLl() {
        return ll;
    }

    @Override
    public final int getMaxPrecision() {
        return Decimals.MAX_PRECISION;
    }

    @Override
    public final int getMaxScale() {
        return MAX_SCALE;
    }

    /**
     * Gets the scale (number of decimal places) of this decimal number.
     *
     * @return the scale
     */
    public int getScale() {
        return scale;
    }

    /**
     * Gets the storage size of the value in pow2 bytes.
     * E.g., 100 can be stored in 1 byte and gives us 0.
     * The minimum value for each possible size is considered to take more space. The point being
     * that we actually use the minimum value for each possible size as a sentinel value for NULL.
     */
    public int getStorageSize() {
        if (hh >= 0) {
            if (hh == 0 && hl == 0 && lh >= 0) {
                if (lh == 0 && ll >= 0) {
                    if ((ll & 0xFFFFFFFF80000000L) == 0) {
                        if ((ll & 0xFFFF8000L) == 0) {
                            if ((ll & 0xFF80L) == 0) {
                                return 0; // 8-bit
                            }
                            return 1; // 16-bit
                        }
                        return 2; // 32-bit
                    }
                    return 3; // 64-bit
                }
                return 4; // 128-bit
            }
        } else {
            if (hh == -1 && hl == -1 && lh < 0 && (lh != Long.MIN_VALUE || ll != 0)) {
                if (lh == -1 && ll < 0 && ll != Long.MIN_VALUE) {
                    final long c = ll - 1;
                    if ((c & 0xFFFFFFFF80000000L) == 0xFFFFFFFF80000000L) {
                        if ((c & 0xFFFF8000L) == 0xFFFF8000L) {
                            if ((c & 0xFF80L) == 0xFF80L) {
                                return 0; // 8-bit
                            }
                            return 1; // 16-bit
                        }
                        return 2; // 32-bit
                    }
                    return 3; // 64-bit
                }
                return 4; // 128-bit
            }
        }
        return 5; // 256-bit
    }

    public boolean hasOverflowed() {
        return hh >= 0 ? compareTo0(MAX_VALUE.hh, MAX_VALUE.hl, MAX_VALUE.lh, MAX_VALUE.ll) > 0
                : compareTo0(MIN_VALUE.hh, MIN_VALUE.hl, MIN_VALUE.lh, MIN_VALUE.ll) < 0;
    }

    /**
     * Returns the hash code for this Decimal256.
     * The hash code is computed based on all 256 bits of the value and the scale.
     *
     * @return the hash code
     */
    @Override
    public int hashCode() {
        int result = Long.hashCode(hh);
        result = 31 * result + Long.hashCode(hl);
        result = 31 * result + Long.hashCode(lh);
        result = 31 * result + Long.hashCode(ll);
        result = 31 * result + scale;
        return result;
    }

    /**
     * Check if this Decimal256 is negative.
     *
     * @return true if negative, false otherwise
     */
    public boolean isNegative() {
        return hh < 0;
    }

    /**
     * Returns whether this is null or not.
     *
     * @return true if null, false otherwise
     */
    public boolean isNull() {
        return hh == Decimals.DECIMAL256_HH_NULL && hl == Decimals.DECIMAL256_HL_NULL
                && lh == Decimals.DECIMAL256_LH_NULL && ll == Decimals.DECIMAL256_LL_NULL;
    }

    /**
     * Check if this Decimal256 represents zero.
     *
     * @return true if zero, false otherwise
     */
    public boolean isZero() {
        return hh == 0 && hl == 0 && lh == 0 && ll == 0;
    }

    /**
     * In-place modulo operation.
     *
     * @param divisor the Decimal256 to take modulo by
     * @throws NumericException if division by zero occurs
     */
    public void modulo(Decimal256 divisor) {
        modulo(divisor.hh, divisor.hl, divisor.lh, divisor.ll, divisor.scale);
    }

    public void modulo(long divisorHH, long divisorHL, long divisorLH, long divisorLL, int divisorScale) {
        if (isNull()) {
            return;
        }

        if (isNull(divisorHH, divisorHL, divisorLH, divisorLL)) {
            ofNull();
            return;
        }

        if (divisorHH == 0 && divisorHL == 0 && divisorLH == 0 && divisorLL == 0) {
            throw NumericException.instance().put("Division by zero");
        }

        // Result scale should be the larger of the two scales
        int resultScale = Math.max(this.scale, divisorScale);

        // Use simple repeated subtraction for modulo: a % b = a - (a / b) * b
        // First compute integer division (a / b)
        // We store this for later usage
        long thisHH = this.hh;
        long thisHL = this.hl;
        long thisLH = this.lh;
        long thisLL = this.ll;
        int thisScale = this.scale;

        this.divide(divisorHH, divisorHL, divisorLH, divisorLL, divisorScale, 0, RoundingMode.DOWN);

        // Now compute this * divisor
        this.multiply(divisorHH, divisorHL, divisorLH, divisorLL, divisorScale);

        long qHH = this.hh;
        long qHL = this.hl;
        long qLH = this.lh;
        long qLL = this.ll;
        int qScale = this.scale;
        // restore this as a
        this.of(thisHH, thisHL, thisLH, thisLL, thisScale);
        // Finally compute remainder: a - (a / b) * b
        this.subtract(qHH, qHL, qLH, qLL, qScale);

        // Handle scale adjustment
        if (this.scale != resultScale) {
            rescale0(resultScale);
        }
    }

    /**
     * Multiplies this Decimal256 by another Decimal256 in-place.
     * The result scale is the sum of both operands' scales.
     *
     * @param other the Decimal256 to multiply by
     * @throws NumericException if overflow occurs or resulting scale exceeds MAX_SCALE
     */
    public void multiply(Decimal256 other) {
        multiply(other.hh, other.hl, other.lh, other.ll, other.scale);
    }

    /**
     * Multiplies this Decimal256 by another Decimal256 in-place.
     * The result scale is the sum of both operands' scales.
     *
     * @throws NumericException if overflow occurs or resulting scale exceeds MAX_SCALE
     */
    public void multiply(long otherHH, long otherHL, long otherLH, long otherLL, int otherScale) {
        if (isNull()) {
            return;
        }

        if (isNull(otherHH, otherHL, otherLH, otherLL)) {
            ofNull();
            return;
        }

        int finalScale = scale + otherScale;
        validateScale(finalScale);

        boolean isNegative = isNegative() ^ (otherHH < 0);

        if (otherHH < 0) {
            otherLL = ~otherLL + 1;
            long c = otherLL == 0L ? 1L : 0L;
            otherLH = ~otherLH + c;
            c = (c == 1L && otherLH == 0L) ? 1L : 0L;
            otherHL = ~otherHL + c;
            c = (c == 1L && otherHL == 0L) ? 1L : 0L;
            otherHH = ~otherHH + c;
        }

        if (isNegative()) {
            negate();
        }

        if (otherHH != 0 || otherHL < 0) {
            multiply256(otherHH, otherHL, otherLH, otherLL);
        } else if (otherHL != 0 || otherLH < 0) {
            multiply192(otherHL, otherLH, otherLL);
        } else if (otherLH != 0 || otherLL < 0) {
            if (hh == 0 && hl == 0 && lh >= 0) {
                multiply128By128(otherLH, otherLL);
            } else {
                multiply128(otherLH, otherLL);
            }
        } else {
            if (hh != 0 || hl < 0) {
                multiply64(otherLL);
            } else if (hl != 0 || lh < 0) {
                multiply192By64(otherLL);
            } else if (lh != 0 || ll < 0) {
                multiply128By64(otherLL);
            } else {
                multiply64By64(otherLL);
            }
        }

        if (hasUnsignOverflowed()) {
            throw NumericException.instance().put("Overflow in multiplication: result exceeds maximum precision");
        }

        if (isNegative) {
            negate();
        }

        scale = finalScale;
    }

    /**
     * Negates this Decimal256 in-place using two's complement arithmetic.
     * Changes the sign of the decimal number (positive becomes negative and vice versa).
     */
    public void negate() {
        if (isNull()) {
            return;
        }
        negateNonNull();
    }

    /**
     * Negates this Decimal256 in-place using two's complement arithmetic.
     * Changes the sign of the decimal number (positive becomes negative and vice versa).
     */
    public void negateNonNull() {
        ll = ~ll + 1;
        long c = ll == 0L ? 1L : 0L;
        lh = ~lh + c;
        c = (c == 1L && lh == 0L) ? 1L : 0L;
        hl = ~hl + c;
        c = (c == 1L && hl == 0L) ? 1L : 0L;
        hh = ~hh + c;
    }

    /**
     * Sets this Decimal256 to the specified 256-bit value and scale.
     *
     * @param hh    the highest 64 bits (bits 192-255)
     * @param hl    the high 64 bits (bits 128-191)
     * @param lh    the mid 64 bits (bits 64-127)
     * @param ll    the low 64 bits (bits 0-63)
     * @param scale the number of decimal places
     */
    public void of(long hh, long hl, long lh, long ll, int scale) {
        this.hh = hh;
        this.hl = hl;
        this.lh = lh;
        this.ll = ll;
        this.scale = scale;
    }

    /**
     * Sets this Decimal256 to the specified 128-bit value and scale.
     *
     * @param decimal128 is copied to this
     * @param scale      the number of decimal places
     */
    public void of(Decimal128 decimal128, int scale) {
        this.hh = this.hl = decimal128.getHigh() < 0 ? -1L : 0L;
        this.lh = decimal128.getHigh();
        this.ll = decimal128.getLow();
        this.scale = scale;
    }

    /**
     * Set this Decimal256 from a long value with the specified scale.
     *
     * @param value the long value
     * @param scale the desired scale
     */
    public void ofLong(long value, int scale) {
        validateScale(scale);

        this.scale = scale;
        this.ll = value;
        final long s = value < 0 ? -1L : 0L;
        this.lh = s;
        this.hl = s;
        this.hh = s;
    }

    /**
     * Set this Decimal256 to the null value.
     */
    public void ofNull() {
        hh = Decimals.DECIMAL256_HH_NULL;
        hl = Decimals.DECIMAL256_HL_NULL;
        lh = Decimals.DECIMAL256_LH_NULL;
        ll = Decimals.DECIMAL256_LL_NULL;
        scale = 0;
    }

    /**
     * Sets this Decimal256 to the specified 256-bit value, but does not change the scale.
     *
     * @param hh the highest 64 bits (bits 192-255)
     * @param hl the high 64 bits (bits 128-191)
     * @param lh the mid 64 bits (bits 64-127)
     * @param ll the low 64 bits (bits 0-63)
     */
    public void ofRaw(long hh, long hl, long lh, long ll) {
        this.hh = hh;
        this.hl = hl;
        this.lh = lh;
        this.ll = ll;
    }

    /**
     * Sets this Decimal256 to the specified 128-bit value, but does not change the scale.
     *
     * @param high the high 64 bits (bits 64-127)
     * @param low  the low 64 bits (bits 0-63)
     */
    public void ofRaw(long high, long low) {
        long s = high < 0 ? -1L : 0L;
        this.hh = s;
        this.hl = s;
        this.lh = high;
        this.ll = low;
    }

    /**
     * Sets this Decimal256 to the specified 64-bit value, but does not change the scale.
     *
     * @param value the 64 bits (bits 0-63)
     */
    public void ofRaw(long value) {
        long s = value < 0 ? -1L : 0L;
        this.hh = s;
        this.hl = s;
        this.lh = s;
        this.ll = value;
    }

    public void ofRawAddress(long addr) {
        ofRaw(
                Unsafe.getUnsafe().getLong(addr),
                Unsafe.getUnsafe().getLong(addr + 8L),
                Unsafe.getUnsafe().getLong(addr + 16L),
                Unsafe.getUnsafe().getLong(addr + 24L)
        );
    }

    /**
     * Set this Decimal256 to the null value.
     */
    public void ofRawNull() {
        hh = Decimals.DECIMAL256_HH_NULL;
        hl = Decimals.DECIMAL256_HL_NULL;
        lh = Decimals.DECIMAL256_LH_NULL;
        ll = Decimals.DECIMAL256_LL_NULL;
    }

    /**
     * Parses a CharSequence decimal and store the result into the given Decimal256.
     *
     * @param cs is the CharSequence to be parsed
     * @return the precision of the decimal
     */
    public long ofString(CharSequence cs) throws NumericException {
        return ofString(cs, -1, -1);
    }

    /**
     * Parses a CharSequence decimal and store the result into the given Decimal256.
     *
     * @param cs        is the CharSequence to be parsed
     * @param precision is the maximum precision that we allow when parsing or -1 if we don't want a limit
     * @param scale     is the final scale of our decimal, if the string has a bigger scale we will throw a NumericException
     * @return the precision of the decimal
     */
    public long ofString(CharSequence cs, int precision, int scale) throws NumericException {
        return ofString(cs, 0, cs.length(), precision, scale, false, false);
    }

    /**
     * Parses a CharSequence decimal and store the result into the given Decimal256.
     *
     * @param cs        is the CharSequence to be parsed
     * @param precision is the maximum precision that we allow when parsing or -1 if we don't want a limit
     * @param scale     is the final scale of our decimal, if the string has a bigger scale we will throw a NumericException
     * @param strict    determines whether we can strip tailing zeroes (making a different scale from the expected one)
     * @param lossy     allows to remove digits from the decimal after the dot to fit the provided scale
     * @return the precision and scale of the decimal, use {@link Numbers#decodeLowInt} to retrieve the precision and
     * {@link Numbers#decodeHighInt} to retrieve the scale
     */
    public long ofString(CharSequence cs, int lo, int hi, int precision, int scale, boolean strict, boolean lossy) throws NumericException {
        return DecimalParser.parse(this, cs, lo, hi, precision, scale, strict, lossy);
    }

    @Override
    public void ofZero() {
        of(0, 0, 0, 0, 0);
    }

    /**
     * Rescale this Decimal256 in place
     *
     * @param newScale The new scale (must be >= current scale)
     */
    public void rescale(int newScale) {
        if (isNull()) {
            return;
        }

        if (newScale < scale) {
            round(newScale, RoundingMode.UNNECESSARY);
            return;
        }

        rescale0(newScale);
    }

    /**
     * Round to specified scale.
     *
     * @param targetScale  the target scale
     * @param roundingMode the rounding mode
     * @throws NumericException if target scale is invalid
     */
    public void round(int targetScale, RoundingMode roundingMode) {
        if (isNull()) {
            return;
        }

        if (targetScale == this.scale) {
            // No rounding needed
            return;
        }

        validateScale(targetScale);

        if (isZero()) {
            this.scale = targetScale;
            return;
        }

        if (this.scale < targetScale) {
            boolean isNegative = isNegative();
            if (isNegative) {
                negateNonNull();
            }

            // Need to increase scale (add trailing zeros)
            int scaleIncrease = targetScale - this.scale;
            multiplyByPowerOf10InPlace(scaleIncrease);
            this.scale = targetScale;

            if (isNegative) {
                negateNonNull();
            }
            return;
        }

        // TODO: optimize the rounding logic by replacing the division by a multiply + shift
        final long hh = this.hh;
        final long hl = this.hl;
        final long lh = this.lh;
        final long ll = this.ll;
        final int scale = this.scale;
        try {
            divide(divider, hh, hl, lh, ll, scale, 0L, 0L, 0L, 1L, 0, this, targetScale, roundingMode);
        } catch (NumericException e) {
            // restore dividend value
            of(hh, hl, lh, ll, scale);
            throw e;
        }
    }

    /**
     * Set the scale forcefully without doing any rescaling operations
     */
    public void setScale(int scale) {
        this.scale = scale;
    }

    /**
     * In-place subtraction.
     *
     * @param other the Decimal256 to subtract
     * @throws NumericException if overflow occurs
     */
    public void subtract(Decimal256 other) {
        subtract(other.hh, other.hl, other.lh, other.ll, other.scale);
    }

    /**
     * In-place subtraction using raw 256-bit values.
     *
     * @param bHH    the subtrahend's highest 64 bits
     * @param bHL    the subtrahend's high 64 bits
     * @param bLH    the subtrahend's mid 64 bits
     * @param bLL    the subtrahend's low 64 bits
     * @param bScale the subtrahend's scale
     * @throws NumericException if overflow occurs
     */
    public void subtract(long bHH, long bHL, long bLH, long bLL, int bScale) {
        if (isNull()) {
            return;
        }

        if (isNull(bHH, bHL, bLH, bLL)) {
            ofNull();
            return;
        }

        // Negate other and perform addition
        if (bHH != 0 || bHL != 0 || bLH != 0 || bLL != 0) {
            bLL = ~bLL + 1;
            long c = bLL == 0L ? 1L : 0L;
            bLH = ~bLH + c;
            c = (c == 1L && bLH == 0L) ? 1L : 0L;
            bHL = ~bHL + c;
            c = (c == 1L && bHL == 0L) ? 1L : 0L;
            bHH = ~bHH + c;

            add(this, hh, hl, lh, ll, scale, bHH, bHL, bLH, bLL, bScale);
        }
    }

    /**
     * Subtracts a specific multiplier of a power of ten from the current 256-bit value.
     * This method modifies the value in-place by subtracting (multiplier * 10^pow).
     * <p>
     * Used in conjunction with getDigitAtPowerOfTen to extract individual digits:
     * 1. Call getDigitAtPowerOfTen to get the digit at a specific power
     * 2. Call this method to subtract that digit's contribution
     * 3. Repeat for the next lower power
     *
     * @param pow        the power of ten position
     * @param multiplier the digit to subtract (1-9, or 0 for no-op)
     */
    public void subtractPowerOfTenMultiple(int pow, int multiplier) {
        if (multiplier == 0 || multiplier > 9) {
            return;
        }

        // Get the value to subtract from POWERS_TEN_TABLE
        // Each power has 9 entries (for multipliers 1-9), each entry has 4 longs
        int offset = (multiplier - 1) * 4;

        // Perform 256-bit subtraction using two's complement
        // First, negate the value to subtract (two's complement)
        long bLL = ~POWERS_TEN_TABLE[pow][offset + 3] + 1;
        long c = bLL == 0L ? 1L : 0L;
        long r = ll + bLL;
        long carry = hasCarry(ll, r) ? 1L : 0L;
        ll = r;

        long bLH = ~POWERS_TEN_TABLE[pow][offset + 2] + c;
        c = (c == 1L && bLH == 0L) ? 1L : 0L;
        long t = lh + carry;
        carry = hasCarry(lh, t) ? 1L : 0L;
        r = t + bLH;
        carry |= hasCarry(t, r) ? 1L : 0L;
        lh = r;

        long bHL = ~POWERS_TEN_TABLE[pow][offset + 1] + c;
        c = (c == 1L && bHL == 0L) ? 1L : 0L;
        t = hl + carry;
        carry = hasCarry(hl, t) ? 1L : 0L;
        r = t + bHL;
        carry |= hasCarry(t, r) ? 1L : 0L;
        hl = r;

        long bHH = ~POWERS_TEN_TABLE[pow][offset] + c;
        hh = hh + carry + bHH;
    }

    /**
     * Convert to BigDecimal.
     *
     * @return BigDecimal representation
     */
    public BigDecimal toBigDecimal() {
        if (isZero() && scale < ZERO_SCALED.length) {
            return ZERO_SCALED[scale];
        }
        return toBigDecimal(hh, hl, lh, ll, scale);
    }

    @Override
    public void toDecimal256(Decimal256 decimal256) {
        decimal256.of(hh, hl, lh, ll, scale);
    }

    /**
     * Convert to double (may lose precision).
     *
     * @return double representation
     */
    public double toDouble() {
        return toBigDecimal().doubleValue();
    }

    /**
     * Writes the string representation of this Decimal256 to the specified CharSink.
     * The output format is a plain decimal string without scientific notation.
     *
     * @param sink the CharSink to write to
     */
    @Override
    public void toSink(@NotNull CharSink<?> sink) {
        toSink(sink, hh, hl, lh, ll, scale);
    }

    /**
     * Returns the string representation of this Decimal256.
     * The output format is a plain decimal string without scientific notation.
     *
     * @return string representation of this decimal number
     */
    @Override
    public String toString() {
        StringSink sink = new StringSink();
        toSink(sink);
        return sink.toString();
    }

    /**
     * In-place addition.
     *
     * @throws NumericException if overflow occurs
     */
    public void uncheckedAdd(Decimal256 other) {
        uncheckedAdd(this, hh, hl, lh, ll, other.hh, other.hl, other.lh, other.ll);
    }

    private static void add(Decimal256 result,
                            long aHH, long aHL, long aLH, long aLL, int aScale,
                            long bHH, long bHL, long bLH, long bLL, int bScale) {
        result.scale = aScale;
        if (aScale < bScale) {
            // We need to rescale A to the same scale as B
            result.of(aHH, aHL, aLH, aLL, aScale);
            result.rescale0(bScale);
            aHH = result.hh;
            aHL = result.hl;
            aLH = result.lh;
            aLL = result.ll;
        } else if (aScale > bScale) {
            // We need to rescale B to the same scale as A
            result.of(bHH, bHL, bLH, bLL, bScale);
            result.rescale0(aScale);
            bHH = result.hh;
            bHL = result.hl;
            bLH = result.lh;
            bLL = result.ll;
        }

        // Perform 256-bit addition
        long r = aLL + bLL;
        long carry = hasCarry(aLL, r) ? 1L : 0L;
        result.ll = r;

        long t = aLH + carry;
        carry = hasCarry(aLH, t) ? 1L : 0L;
        r = t + bLH;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.lh = r;

        t = aHL + carry;
        carry = hasCarry(aHL, t) ? 1L : 0L;
        r = t + bHL;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.hl = r;

        t = aHH + carry;
        r = t + bHH;
        if (((bHH ^ r) & (t ^ r)) < 0L || ((aHH ^ t) & (carry ^ t)) < 0L) {
            throw NumericException.instance().put("Overflow in addition: result exceeds 256-bit capacity");
        }
        result.hh = r;
        if (result.hasOverflowed()) {
            throw NumericException.instance().put("Overflow in addition: result exceeds maximum precision");
        }
    }

    private static int compareToPowerOfTen(long aHH, long aHL, long aLH, long aLL, int pow, int multiplier) {
        final int offset = (multiplier - 1) * 4;
        long bHH = POWERS_TEN_TABLE[pow][offset];
        if (aHH != bHH) {
            return Long.compare(aHH, bHH);
        }
        long bHL = POWERS_TEN_TABLE[pow][offset + 1];
        if (aHL != bHL) {
            return Long.compareUnsigned(aHL, bHL);
        }
        long bLH = POWERS_TEN_TABLE[pow][offset + 2];
        if (aLH != bLH) {
            return Long.compareUnsigned(aLH, bLH);
        }
        long bLL = POWERS_TEN_TABLE[pow][offset + 3];
        return Long.compareUnsigned(aLL, bLL);
    }

    private static void putLongIntoBytes(byte[] bytes, int offset, long value) {
        for (int i = 0; i < 8; i++) {
            bytes[offset + i] = (byte) (value >>> ((7 - i) * 8));
        }
    }

    private static void uncheckedAdd(Decimal256 result,
                                     long aHH, long aHL, long aLH, long aLL,
                                     long bHH, long bHL, long bLH, long bLL) {

        // Perform 256-bit addition
        long r = aLL + bLL;
        long carry = hasCarry(aLL, r) ? 1L : 0L;
        result.ll = r;

        long t = aLH + carry;
        carry = hasCarry(aLH, t) ? 1L : 0L;
        r = t + bLH;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.lh = r;

        t = aHL + carry;
        carry = hasCarry(aHL, t) ? 1L : 0L;
        r = t + bHL;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.hl = r;

        t = aHH + carry;
        r = t + bHH;
        if (((bHH ^ r) & (t ^ r)) < 0L || ((aHH ^ t) & (carry ^ t)) < 0L) {
            throw NumericException.instance().put("Overflow in addition: result exceeds 256-bit capacity");
        }
        result.hh = r;
    }

    private static void uncheckedAdd(Decimal256 result, long bHH, long bHL, long bLH, long bLL) {
        long r = result.ll + bLL;
        long carry = hasCarry(result.ll, r) ? 1L : 0L;
        result.ll = r;

        long t = result.lh + carry;
        carry = hasCarry(result.lh, t) ? 1L : 0L;
        r = t + bLH;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.lh = r;

        t = result.hl + carry;
        carry = hasCarry(result.hl, t) ? 1L : 0L;
        r = t + bHL;
        carry |= hasCarry(t, r) ? 1L : 0L;
        result.hl = r;
        result.hh = result.hh + carry + bHH;
    }

    /**
     * Validates that the scale is within the allowed range.
     *
     * @param scale the scale to validate
     * @throws NumericException if scale is invalid
     */
    private static void validateScale(int scale) {
        if (scale < 0 || scale > MAX_SCALE) {
            throw NumericException.instance().put("Invalid scale: " + scale);
        }
    }

    private int compareTo0(long bHH, long bHL, long bLH, long bLL) {
        return compare(hh, hl, lh, ll, bHH, bHL, bLH, bLL);
    }

    private boolean hasUnsignOverflowed() {
        return compareTo0(MAX_VALUE.hh, MAX_VALUE.hl, MAX_VALUE.lh, MAX_VALUE.ll) > 0;
    }

    private void multiply128(long h, long l) {
        // Perform 256-bit × 128-bit multiplication
        // Result is at most 384 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = hh >>> 32;
        long a6 = hh & 0xFFFFFFFFL;
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b3 = h >>> 32;
        long b2 = h & 0xFFFFFFFFL;
        long b1 = l >>> 32;
        long b0 = l & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p42 = a4 * b2;
        long p43 = a4 * b3;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p52 = a5 * b2;
        long p53 = a5 * b3;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p62 = a6 * b2;
        long p63 = a6 * b3;
        long p70 = a7 * b0;
        long p71 = a7 * b1;
        long p72 = a7 * b2;
        long p73 = a7 * b3;

        long overflow = p53 | p62 | p63 | p71 | p72 | p73;
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (128-bit × 128-bit): product exceeds 256-bit capacity");
        }

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL) + (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p23 >>> 32) + (p32 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p33 & 0xFFFFFFFFL) + (p42 & 0xFFFFFFFFL) + (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p33 >>> 32) + (p42 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p43 & 0xFFFFFFFFL) + (p52 & 0xFFFFFFFFL) + (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);
        overflow = (r7 >>> 31) | (p43 >>> 32) | (p52 >>> 32) | (p61 >>> 32) | (p70 >>> 32);
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (128-bit × 128-bit): product exceeds 256-bit capacity");
        }

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply128By128(long h, long l) {
        // Perform 256-bit × 128-bit multiplication
        // Result is at most 384 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b3 = h >>> 32;
        long b2 = h & 0xFFFFFFFFL;
        long b1 = l >>> 32;
        long b0 = l & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) +
                (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p23 >>> 32) + (p32 >>> 32) +
                (p33 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p33 >>> 32);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply128By64(long multiplier) {
        // Perform 128-bit × 64-bit multiplication

        // Split this into 32-bit parts
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b1 = multiplier >>> 32;
        long b0 = multiplier & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p30 = a3 * b0;
        long p31 = a3 * b1;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p31 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p31 >>> 32);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = 0;
    }

    private void multiply128Unchecked(long h, long l) {
        // Perform 256-bit × 128-bit multiplication
        // Result is at most 384 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = hh >>> 32;
        long a6 = hh & 0xFFFFFFFFL;
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b3 = h >>> 32;
        long b2 = h & 0xFFFFFFFFL;
        long b1 = l >>> 32;
        long b0 = l & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p42 = a4 * b2;
        long p43 = a4 * b3;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p52 = a5 * b2;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p70 = a7 * b0;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL) + (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p23 >>> 32) + (p32 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p33 & 0xFFFFFFFFL) + (p42 & 0xFFFFFFFFL) + (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p33 >>> 32) + (p42 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p43 & 0xFFFFFFFFL) + (p52 & 0xFFFFFFFFL) + (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply192(long h, long m, long l) {
        // Perform 256-bit × 192-bit multiplication
        // Result is at most 448 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = hh >>> 32;
        long a6 = hh & 0xFFFFFFFFL;
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b5 = h >>> 32;
        long b4 = h & 0xFFFFFFFFL;
        long b3 = m >>> 32;
        long b2 = m & 0xFFFFFFFFL;
        long b1 = l >>> 32;
        long b0 = l & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p04 = a0 * b4;
        long p05 = a0 * b5;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p14 = a1 * b4;
        long p15 = a1 * b5;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p24 = a2 * b4;
        long p25 = a2 * b5;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;
        long p34 = a3 * b4;
        long p35 = a3 * b5;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p42 = a4 * b2;
        long p43 = a4 * b3;
        long p44 = a4 * b4;
        long p45 = a4 * b5;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p52 = a5 * b2;
        long p53 = a5 * b3;
        long p54 = a5 * b4;
        long p55 = a5 * b5;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p62 = a6 * b2;
        long p63 = a6 * b3;
        long p64 = a6 * b4;
        long p65 = a6 * b5;
        long p70 = a7 * b0;
        long p71 = a7 * b1;
        long p72 = a7 * b2;
        long p73 = a7 * b3;
        long p74 = a7 * b4;
        long p75 = a7 * b5;

        long overflow = p35 | p44 | p45 | p53 | p54 | p55 | p62 | p63 | p64 | p65 | p71 | p72 | p73 | p74 | p75;
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (256-bit × 128-bit): product exceeds 256-bit capacity");
        }

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p04 & 0xFFFFFFFFL) + (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p04 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p05 & 0xFFFFFFFFL) + (p14 & 0xFFFFFFFFL) + (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL) + (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p05 >>> 32) + (p14 >>> 32) + (p23 >>> 32) + (p32 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p15 & 0xFFFFFFFFL) + (p24 & 0xFFFFFFFFL) + (p33 & 0xFFFFFFFFL) + (p42 & 0xFFFFFFFFL) + (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p15 >>> 32) + (p24 >>> 32) + (p33 >>> 32) + (p42 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p25 & 0xFFFFFFFFL) + (p34 & 0xFFFFFFFFL) + (p43 & 0xFFFFFFFFL) + (p52 & 0xFFFFFFFFL) + (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);
        overflow = (r7 >>> 31) | (p25 >>> 32) | (p34 >>> 32) | (p43 >>> 32) | (p52 >>> 32) | (p61 >>> 32) | (p70 >>> 32);
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (256-bit × 128-bit): product exceeds 256-bit capacity");
        }

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply192By64(long multiplier) {
        // Perform 192-bit × 64-bit multiplication

        // Split this into 32-bit parts
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b1 = multiplier >>> 32;
        long b0 = multiplier & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p50 = a5 * b0;
        long p51 = a5 * b1;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p51 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p51 >>> 32);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply192Unchecked(long h, long m, long l) {
        // Perform 256-bit × 192-bit multiplication
        // Result is at most 448 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = hh >>> 32;
        long a6 = hh & 0xFFFFFFFFL;
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b5 = h >>> 32;
        long b4 = h & 0xFFFFFFFFL;
        long b3 = m >>> 32;
        long b2 = m & 0xFFFFFFFFL;
        long b1 = l >>> 32;
        long b0 = l & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p04 = a0 * b4;
        long p05 = a0 * b5;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p14 = a1 * b4;
        long p15 = a1 * b5;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p24 = a2 * b4;
        long p25 = a2 * b5;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;
        long p34 = a3 * b4;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p42 = a4 * b2;
        long p43 = a4 * b3;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p52 = a5 * b2;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p70 = a7 * b0;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p04 & 0xFFFFFFFFL) + (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p04 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p05 & 0xFFFFFFFFL) + (p14 & 0xFFFFFFFFL) + (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL) + (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p05 >>> 32) + (p14 >>> 32) + (p23 >>> 32) + (p32 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p15 & 0xFFFFFFFFL) + (p24 & 0xFFFFFFFFL) + (p33 & 0xFFFFFFFFL) + (p42 & 0xFFFFFFFFL) + (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p15 >>> 32) + (p24 >>> 32) + (p33 >>> 32) + (p42 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p25 & 0xFFFFFFFFL) + (p34 & 0xFFFFFFFFL) + (p43 & 0xFFFFFFFFL) + (p52 & 0xFFFFFFFFL) + (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply256(long hh, long hl, long lh, long ll) {
        // Perform 256-bit × 256-bit multiplication
        // Result is at most 512 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = this.hh >>> 32;
        long a6 = this.hh & 0xFFFFFFFFL;
        long a5 = this.hl >>> 32;
        long a4 = this.hl & 0xFFFFFFFFL;
        long a3 = this.lh >>> 32;
        long a2 = this.lh & 0xFFFFFFFFL;
        long a1 = this.ll >>> 32;
        long a0 = this.ll & 0xFFFFFFFFL;

        long b7 = hh >>> 32;
        long b6 = hh & 0xFFFFFFFFL;
        long b5 = hl >>> 32;
        long b4 = hl & 0xFFFFFFFFL;
        long b3 = lh >>> 32;
        long b2 = lh & 0xFFFFFFFFL;
        long b1 = ll >>> 32;
        long b0 = ll & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p04 = a0 * b4;
        long p05 = a0 * b5;
        long p06 = a0 * b6;
        long p07 = a0 * b7;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p14 = a1 * b4;
        long p15 = a1 * b5;
        long p16 = a1 * b6;
        long p17 = a1 * b7;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p24 = a2 * b4;
        long p25 = a2 * b5;
        long p26 = a2 * b6;
        long p27 = a2 * b7;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;
        long p34 = a3 * b4;
        long p35 = a3 * b5;
        long p36 = a3 * b6;
        long p37 = a3 * b7;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p42 = a4 * b2;
        long p43 = a4 * b3;
        long p44 = a4 * b4;
        long p45 = a4 * b5;
        long p46 = a4 * b6;
        long p47 = a4 * b7;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p52 = a5 * b2;
        long p53 = a5 * b3;
        long p54 = a5 * b4;
        long p55 = a5 * b5;
        long p56 = a5 * b6;
        long p57 = a5 * b7;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p62 = a6 * b2;
        long p63 = a6 * b3;
        long p64 = a6 * b4;
        long p65 = a6 * b5;
        long p66 = a6 * b6;
        long p67 = a6 * b7;
        long p70 = a7 * b0;
        long p71 = a7 * b1;
        long p72 = a7 * b2;
        long p73 = a7 * b3;
        long p74 = a7 * b4;
        long p75 = a7 * b5;
        long p76 = a7 * b6;
        long p77 = a7 * b7;

        long overflow = p17 | p26 | p27 | p35 | p36 | p37 | p44 | p45 | p46 | p47 | p53 | p54 | p55 | p56 | p57 |
                p62 | p63 | p64 | p65 | p66 | p67 | p71 | p72 | p73 | p74 | p75 | p76 | p77;
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (256-bit × 256-bit): product exceeds 256-bit capacity");
        }

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p04 & 0xFFFFFFFFL) + (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p04 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p05 & 0xFFFFFFFFL) + (p14 & 0xFFFFFFFFL) + (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL) + (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p05 >>> 32) + (p14 >>> 32) + (p23 >>> 32) + (p32 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p06 & 0xFFFFFFFFL) + (p15 & 0xFFFFFFFFL) + (p24 & 0xFFFFFFFFL) + (p33 & 0xFFFFFFFFL) + (p42 & 0xFFFFFFFFL) + (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p06 >>> 32) + (p15 >>> 32) + (p24 >>> 32) + (p33 >>> 32) + (p42 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p07 & 0xFFFFFFFFL) + (p16 & 0xFFFFFFFFL) + (p25 & 0xFFFFFFFFL) + (p34 & 0xFFFFFFFFL) + (p43 & 0xFFFFFFFFL) + (p52 & 0xFFFFFFFFL) + (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);
        overflow = (r7 >>> 31) | (p07 >>> 32) | (p16 >>> 32) | (p25 >>> 32) | (p34 >>> 32) | (p43 >>> 32) | (p52 >>> 32) | (p61 >>> 32) | (p70 >>> 32);
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (256-bit × 256-bit): product exceeds 256-bit capacity");
        }

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply256Unchecked(long hh, long hl, long lh, long ll) {
        // Perform 256-bit × 256-bit multiplication
        // Result is at most 512 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = this.hh >>> 32;
        long a6 = this.hh & 0xFFFFFFFFL;
        long a5 = this.hl >>> 32;
        long a4 = this.hl & 0xFFFFFFFFL;
        long a3 = this.lh >>> 32;
        long a2 = this.lh & 0xFFFFFFFFL;
        long a1 = this.ll >>> 32;
        long a0 = this.ll & 0xFFFFFFFFL;

        long b7 = hh >>> 32;
        long b6 = hh & 0xFFFFFFFFL;
        long b5 = hl >>> 32;
        long b4 = hl & 0xFFFFFFFFL;
        long b3 = lh >>> 32;
        long b2 = lh & 0xFFFFFFFFL;
        long b1 = ll >>> 32;
        long b0 = ll & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p02 = a0 * b2;
        long p03 = a0 * b3;
        long p04 = a0 * b4;
        long p05 = a0 * b5;
        long p06 = a0 * b6;
        long p07 = a0 * b7;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p12 = a1 * b2;
        long p13 = a1 * b3;
        long p14 = a1 * b4;
        long p15 = a1 * b5;
        long p16 = a1 * b6;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p22 = a2 * b2;
        long p23 = a2 * b3;
        long p24 = a2 * b4;
        long p25 = a2 * b5;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p32 = a3 * b2;
        long p33 = a3 * b3;
        long p34 = a3 * b4;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p42 = a4 * b2;
        long p43 = a4 * b3;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p52 = a5 * b2;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p70 = a7 * b0;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p02 & 0xFFFFFFFFL) + (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p02 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p03 & 0xFFFFFFFFL) + (p12 & 0xFFFFFFFFL) + (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p03 >>> 32) + (p12 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p04 & 0xFFFFFFFFL) + (p13 & 0xFFFFFFFFL) + (p22 & 0xFFFFFFFFL) + (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p04 >>> 32) + (p13 >>> 32) + (p22 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p05 & 0xFFFFFFFFL) + (p14 & 0xFFFFFFFFL) + (p23 & 0xFFFFFFFFL) + (p32 & 0xFFFFFFFFL) + (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p05 >>> 32) + (p14 >>> 32) + (p23 >>> 32) + (p32 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p06 & 0xFFFFFFFFL) + (p15 & 0xFFFFFFFFL) + (p24 & 0xFFFFFFFFL) + (p33 & 0xFFFFFFFFL) + (p42 & 0xFFFFFFFFL) + (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p06 >>> 32) + (p15 >>> 32) + (p24 >>> 32) + (p33 >>> 32) + (p42 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p07 & 0xFFFFFFFFL) + (p16 & 0xFFFFFFFFL) + (p25 & 0xFFFFFFFFL) + (p34 & 0xFFFFFFFFL) + (p43 & 0xFFFFFFFFL) + (p52 & 0xFFFFFFFFL) + (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    private void multiply64(long multiplier) {
        // Perform 256-bit × 64-bit multiplication
        // Result is at most 320 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = hh >>> 32;
        long a6 = hh & 0xFFFFFFFFL;
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b1 = multiplier >>> 32;
        long b0 = multiplier & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p70 = a7 * b0;
        long p71 = a7 * b1;
        if (p71 != 0) {
            throw NumericException.instance().put("Overflow in multiplication (256-bit × 64-bit): product exceeds 256-bit capacity");
        }

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);
        long overflow = (r7 >>> 31) | (p61 >>> 32) | (p70 >>> 32);
        if (overflow != 0) {
            throw NumericException.instance().put("Overflow in multiplication (256-bit × 64-bit): product exceeds 256-bit capacity");
        }

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    /* --------------------- helpers (no private tables required) --------------------- */

    private void multiply64By64(long multiplier) {
        // Perform 64-bit × 64-bit multiplication

        // Split this into 32-bit parts
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b1 = multiplier >>> 32;
        long b0 = multiplier & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p10 = a1 * b0;
        long p11 = a1 * b1;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p11 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p11 >>> 32);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = 0;
        this.hh = 0;
    }

    private void multiply64Unchecked(long multiplier) {
        // Perform 256-bit × 64-bit multiplication
        // Result is at most 320 bits, but we keep only the lower 256 bits

        // Split this into eight 32-bit parts
        long a7 = hh >>> 32;
        long a6 = hh & 0xFFFFFFFFL;
        long a5 = hl >>> 32;
        long a4 = hl & 0xFFFFFFFFL;
        long a3 = lh >>> 32;
        long a2 = lh & 0xFFFFFFFFL;
        long a1 = ll >>> 32;
        long a0 = ll & 0xFFFFFFFFL;

        long b1 = multiplier >>> 32;
        long b0 = multiplier & 0xFFFFFFFFL;

        // Compute all partial products
        long p00 = a0 * b0;
        long p01 = a0 * b1;
        long p10 = a1 * b0;
        long p11 = a1 * b1;
        long p20 = a2 * b0;
        long p21 = a2 * b1;
        long p30 = a3 * b0;
        long p31 = a3 * b1;
        long p40 = a4 * b0;
        long p41 = a4 * b1;
        long p50 = a5 * b0;
        long p51 = a5 * b1;
        long p60 = a6 * b0;
        long p61 = a6 * b1;
        long p70 = a7 * b0;

        // Gather results into 256-bit result
        long r0 = (p00 & 0xFFFFFFFFL);
        long r1 = (p00 >>> 32) + (p01 & 0xFFFFFFFFL) + (p10 & 0xFFFFFFFFL);
        long r2 = (r1 >>> 32) + (p01 >>> 32) + (p10 >>> 32) +
                (p11 & 0xFFFFFFFFL) + (p20 & 0xFFFFFFFFL);
        long r3 = (r2 >>> 32) + (p11 >>> 32) + (p20 >>> 32) +
                (p21 & 0xFFFFFFFFL) + (p30 & 0xFFFFFFFFL);
        long r4 = (r3 >>> 32) + (p21 >>> 32) + (p30 >>> 32) +
                (p31 & 0xFFFFFFFFL) + (p40 & 0xFFFFFFFFL);
        long r5 = (r4 >>> 32) + (p31 >>> 32) + (p40 >>> 32) +
                (p41 & 0xFFFFFFFFL) + (p50 & 0xFFFFFFFFL);
        long r6 = (r5 >>> 32) + (p41 >>> 32) + (p50 >>> 32) +
                (p51 & 0xFFFFFFFFL) + (p60 & 0xFFFFFFFFL);
        long r7 = (r6 >>> 32) + (p51 >>> 32) + (p60 >>> 32) +
                (p61 & 0xFFFFFFFFL) + (p70 & 0xFFFFFFFFL);

        this.ll = (r0 & 0xFFFFFFFFL) | ((r1 & 0xFFFFFFFFL) << 32);
        this.lh = (r2 & 0xFFFFFFFFL) | ((r3 & 0xFFFFFFFFL) << 32);
        this.hl = (r4 & 0xFFFFFFFFL) | ((r5 & 0xFFFFFFFFL) << 32);
        this.hh = (r6 & 0xFFFFFFFFL) | ((r7 & 0xFFFFFFFFL) << 32);
    }

    /**
     * Multiply this (unsigned) by 10^n in place
     */
    private void multiplyByPowerOf10InPlace(int n) {
        if (n <= 0 || isZero()) {
            return;
        }

        // For small powers, use lookup table
        if (n < 18) {
            long multiplier = POWERS_TEN_TABLE[n][3];
            // Special case: if high is 0, use simple 64-bit multiplication
            if (hh == 0 && hl == 0 && lh == 0 && ll > 0) {
                // Check if result will overflow 64 bits
                if (ll <= Long.MAX_VALUE / multiplier) {
                    ll *= multiplier;
                    return;
                }
            }
        }

        if (n >= 76) {
            throw NumericException.instance().put("Overflow in scale adjustment: multiplying by 10^" + n + " exceeds 256-bit capacity");
        }

        // For larger powers, break down into smaller chunks, first check the threshold to ensure that we won't overflow
        // and then apply either a fast multiplyBy64 or multiplyBy128 depending on high/low.
        // The bound checks for these tables already happens at the beginning of the method.
        final long[] thresholds = POWERS_TEN_TABLE_THRESHOLDS[n - 1];
        if (compareTo0(thresholds[0], thresholds[1], thresholds[2], thresholds[3]) > 0) {
            throw NumericException.instance().put("Overflow in scale adjustment: multiplying by 10^" + n + " exceeds 256-bit capacity");
        }

        final long multiplierHH = POWERS_TEN_TABLE[n][0];
        final long multiplierHL = POWERS_TEN_TABLE[n][1];
        final long multiplierLH = POWERS_TEN_TABLE[n][2];
        final long multiplierLL = POWERS_TEN_TABLE[n][3];

        if (multiplierHH != 0L /* || multiplierHL < 0L */) { // multiplierHL always false, keep comment to ack
            multiply256Unchecked(multiplierHH, multiplierHL, multiplierLH, multiplierLL);
        } else if (multiplierHL != 0L /* || multiplierLH < 0L */) { // multiplierLH always false, keep comment to ack
            multiply192Unchecked(multiplierHL, multiplierLH, multiplierLL);
        } else if (multiplierLH != 0L || multiplierLL < 0L) {
            multiply128Unchecked(multiplierLH, multiplierLL);
        } else {
            multiply64Unchecked(multiplierLL);
        }
    }

    /**
     * Rescale this Decimal256 in place without checks
     *
     * @param newScale The new scale (must be >= current scale)
     */
    private void rescale0(int newScale) {
        int scaleDiff = newScale - this.scale;

        boolean isNegative = isNegative();
        if (isNegative) {
            negateNonNull();
        }

        // Multiply by 10^scaleDiff
        multiplyByPowerOf10InPlace(scaleDiff);

        if (isNegative) {
            negateNonNull();
        }

        this.scale = newScale;
    }

    /**
     * Set this Decimal256 from a byte array representation.
     *
     * @param bytes the byte array
     */
    private void setFromByteArray(byte[] bytes) {
        // Clear all fields first
        this.hh = 0;
        this.hl = 0;
        this.lh = 0;
        this.ll = 0;

        // Fill from the least significant bytes
        int byteIndex = bytes.length - 1;

        // Fill low 64 bits
        for (int i = 0; i < 8 && byteIndex >= 0; i++, byteIndex--) {
            this.ll |= ((long) (bytes[byteIndex] & 0xFF)) << (i * 8);
        }

        // Fill mid 64 bits
        for (int i = 0; i < 8 && byteIndex >= 0; i++, byteIndex--) {
            this.lh |= ((long) (bytes[byteIndex] & 0xFF)) << (i * 8);
        }

        // Fill high 64 bits
        for (int i = 0; i < 8 && byteIndex >= 0; i++, byteIndex--) {
            this.hl |= ((long) (bytes[byteIndex] & 0xFF)) << (i * 8);
        }

        // Fill highest 64 bits
        for (int i = 0; i < 8 && byteIndex >= 0; i++, byteIndex--) {
            this.hh |= ((long) (bytes[byteIndex] & 0xFF)) << (i * 8);
        }

        if (hasUnsignOverflowed()) {
            throw NumericException.instance().put("Overflow in addition: result exceeds maximum precision");
        }
    }

    static {
        for (int i = 0; i < ZERO_SCALED.length; i++) {
            ZERO_SCALED[i] = toBigDecimal(0, 0, 0, 0, i);
        }
    }
}
